blob: df6e210f0368dc7408d9f1342926632d2fb0d4ea [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"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
raviteja-b06578432020-02-03 12:50:42 -060030#include <dump_offload.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070031#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070032#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070033#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080034#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070035
36namespace redfish
37{
38
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070039constexpr char const *crashdumpObject = "com.intel.crashdump";
40constexpr char const *crashdumpPath = "/com/intel/crashdump";
41constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
42constexpr char const *crashdumpInterface = "com.intel.crashdump";
43constexpr char const *deleteAllInterface =
44 "xyz.openbmc_project.Collection.DeleteAll";
45constexpr char const *crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070046 "com.intel.crashdump.OnDemand";
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070047constexpr char const *crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070048 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070049
Jason M. Bills4851d452019-03-28 11:27:48 -070050namespace message_registries
51{
52static const Message *getMessageFromRegistry(
53 const std::string &messageKey,
54 const boost::beast::span<const MessageEntry> registry)
55{
56 boost::beast::span<const MessageEntry>::const_iterator messageIt =
57 std::find_if(registry.cbegin(), registry.cend(),
58 [&messageKey](const MessageEntry &messageEntry) {
59 return !std::strcmp(messageEntry.first,
60 messageKey.c_str());
61 });
62 if (messageIt != registry.cend())
63 {
64 return &messageIt->second;
65 }
66
67 return nullptr;
68}
69
70static const Message *getMessage(const std::string_view &messageID)
71{
72 // Redfish MessageIds are in the form
73 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
74 // the right Message
75 std::vector<std::string> fields;
76 fields.reserve(4);
77 boost::split(fields, messageID, boost::is_any_of("."));
78 std::string &registryName = fields[0];
79 std::string &messageKey = fields[3];
80
81 // Find the right registry and check it for the MessageKey
82 if (std::string(base::header.registryPrefix) == registryName)
83 {
84 return getMessageFromRegistry(
85 messageKey, boost::beast::span<const MessageEntry>(base::registry));
86 }
87 if (std::string(openbmc::header.registryPrefix) == registryName)
88 {
89 return getMessageFromRegistry(
90 messageKey,
91 boost::beast::span<const MessageEntry>(openbmc::registry));
92 }
93 return nullptr;
94}
95} // namespace message_registries
96
James Feistf6150402019-01-08 10:36:20 -080097namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070098
Andrew Geisslercb92c032018-08-17 07:56:14 -070099using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500100 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
101 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700102
103using GetManagedObjectsType = boost::container::flat_map<
104 sdbusplus::message::object_path,
105 boost::container::flat_map<std::string, GetManagedPropertyType>>;
106
107inline std::string translateSeverityDbusToRedfish(const std::string &s)
108{
109 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
110 {
111 return "Critical";
112 }
113 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
114 {
115 return "Critical";
116 }
117 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
118 {
119 return "OK";
120 }
121 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
122 {
123 return "Critical";
124 }
125 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
126 {
127 return "Critical";
128 }
129 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
130 {
131 return "OK";
132 }
133 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
134 {
135 return "OK";
136 }
137 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
138 {
139 return "Warning";
140 }
141 return "";
142}
143
raviteja-bc9bb6862020-02-03 11:53:32 -0600144inline void deleteSystemDumpEntry(crow::Response &res,
145 const std::string &entryID)
146{
147 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
148
149 auto respHandler = [asyncResp](const boost::system::error_code ec) {
150 BMCWEB_LOG_DEBUG << "System Dump Entry doDelete callback: Done";
151 if (ec)
152 {
153 BMCWEB_LOG_ERROR
154 << "System Dump (DBus) doDelete respHandler got error " << ec;
155 asyncResp->res.result(
156 boost::beast::http::status::internal_server_error);
157 return;
158 }
raviteja-bc9bb6862020-02-03 11:53:32 -0600159 };
160 crow::connections::systemBus->async_method_call(
161 respHandler, "xyz.openbmc_project.Dump.Manager",
162 "/xyz/openbmc_project/dump/entry/" + entryID,
163 "xyz.openbmc_project.Object.Delete", "Delete");
164}
165
Jason M. Bills16428a12018-11-02 12:42:29 -0700166static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800167 const std::string_view &field,
168 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700169{
170 const char *data = nullptr;
171 size_t length = 0;
172 int ret = 0;
173 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700174 ret = sd_journal_get_data(journal, field.data(),
175 reinterpret_cast<const void **>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700176 if (ret < 0)
177 {
178 return ret;
179 }
Ed Tanous39e77502019-03-04 17:35:53 -0800180 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700181 // Only use the content after the "=" character.
182 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
183 return ret;
184}
185
186static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800187 const std::string_view &field, const int &base,
Ed Tanous271584a2019-07-09 16:24:22 -0700188 long int &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700189{
190 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800191 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700192 // Get the metadata from the requested field of the journal entry
193 ret = getJournalMetadata(journal, field, metadata);
194 if (ret < 0)
195 {
196 return ret;
197 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000198 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700199 return ret;
200}
201
ZhikuiRena3316fc2020-01-29 14:58:08 -0800202static bool getTimestampStr(const uint64_t usecSinceEpoch,
203 std::string &entryTimestamp)
Jason M. Bills16428a12018-11-02 12:42:29 -0700204{
ZhikuiRena3316fc2020-01-29 14:58:08 -0800205 time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000);
Jason M. Bills16428a12018-11-02 12:42:29 -0700206 struct tm *loctime = localtime(&t);
207 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700208 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700209 {
210 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
211 }
212 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800213 std::string_view t1(entryTime);
214 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700215 if (t1.size() > 2 && t2.size() > 2)
216 {
217 t1.remove_suffix(2);
218 t2.remove_prefix(t2.size() - 2);
219 }
Ed Tanous39e77502019-03-04 17:35:53 -0800220 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700221 return true;
222}
223
ZhikuiRena3316fc2020-01-29 14:58:08 -0800224static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
225{
226 int ret = 0;
227 uint64_t timestamp = 0;
228 ret = sd_journal_get_realtime_usec(journal, &timestamp);
229 if (ret < 0)
230 {
231 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
232 << strerror(-ret);
233 return false;
234 }
235 return getTimestampStr(timestamp, entryTimestamp);
236}
237
Jason M. Bills16428a12018-11-02 12:42:29 -0700238static bool getSkipParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700239 uint64_t &skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700240{
241 char *skipParam = req.urlParams.get("$skip");
242 if (skipParam != nullptr)
243 {
244 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700245 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700246 if (*skipParam == '\0' || *ptr != '\0')
247 {
248
249 messages::queryParameterValueTypeError(res, std::string(skipParam),
250 "$skip");
251 return false;
252 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700253 }
254 return true;
255}
256
Ed Tanous271584a2019-07-09 16:24:22 -0700257static constexpr const uint64_t maxEntriesPerPage = 1000;
Jason M. Bills16428a12018-11-02 12:42:29 -0700258static bool getTopParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700259 uint64_t &top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700260{
261 char *topParam = req.urlParams.get("$top");
262 if (topParam != nullptr)
263 {
264 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700265 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700266 if (*topParam == '\0' || *ptr != '\0')
267 {
268 messages::queryParameterValueTypeError(res, std::string(topParam),
269 "$top");
270 return false;
271 }
Ed Tanous271584a2019-07-09 16:24:22 -0700272 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700273 {
274
275 messages::queryParameterOutOfRange(
276 res, std::to_string(top), "$top",
277 "1-" + std::to_string(maxEntriesPerPage));
278 return false;
279 }
280 }
281 return true;
282}
283
Jason M. Billse85d6b12019-07-29 17:01:15 -0700284static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
285 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700286{
287 int ret = 0;
288 static uint64_t prevTs = 0;
289 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700290 if (firstEntry)
291 {
292 prevTs = 0;
293 }
294
Jason M. Bills16428a12018-11-02 12:42:29 -0700295 // Get the entry timestamp
296 uint64_t curTs = 0;
297 ret = sd_journal_get_realtime_usec(journal, &curTs);
298 if (ret < 0)
299 {
300 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
301 << strerror(-ret);
302 return false;
303 }
304 // If the timestamp isn't unique, increment the index
305 if (curTs == prevTs)
306 {
307 index++;
308 }
309 else
310 {
311 // Otherwise, reset it
312 index = 0;
313 }
314 // Save the timestamp
315 prevTs = curTs;
316
317 entryID = std::to_string(curTs);
318 if (index > 0)
319 {
320 entryID += "_" + std::to_string(index);
321 }
322 return true;
323}
324
Jason M. Billse85d6b12019-07-29 17:01:15 -0700325static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
326 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700327{
Ed Tanous271584a2019-07-09 16:24:22 -0700328 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700329 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700330 if (firstEntry)
331 {
332 prevTs = 0;
333 }
334
Jason M. Bills95820182019-04-22 16:25:34 -0700335 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700336 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700337 std::tm timeStruct = {};
338 std::istringstream entryStream(logEntry);
339 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
340 {
341 curTs = std::mktime(&timeStruct);
342 }
343 // If the timestamp isn't unique, increment the index
344 if (curTs == prevTs)
345 {
346 index++;
347 }
348 else
349 {
350 // Otherwise, reset it
351 index = 0;
352 }
353 // Save the timestamp
354 prevTs = curTs;
355
356 entryID = std::to_string(curTs);
357 if (index > 0)
358 {
359 entryID += "_" + std::to_string(index);
360 }
361 return true;
362}
363
Jason M. Bills16428a12018-11-02 12:42:29 -0700364static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
Ed Tanous271584a2019-07-09 16:24:22 -0700365 uint64_t &timestamp, uint64_t &index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700366{
367 if (entryID.empty())
368 {
369 return false;
370 }
371 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800372 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700373
374 auto underscorePos = tsStr.find("_");
375 if (underscorePos != tsStr.npos)
376 {
377 // Timestamp has an index
378 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800379 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700380 indexStr.remove_prefix(underscorePos + 1);
381 std::size_t pos;
382 try
383 {
Ed Tanous39e77502019-03-04 17:35:53 -0800384 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700385 }
Ed Tanous271584a2019-07-09 16:24:22 -0700386 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700387 {
388 messages::resourceMissingAtURI(res, entryID);
389 return false;
390 }
Ed Tanous271584a2019-07-09 16:24:22 -0700391 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700392 {
393 messages::resourceMissingAtURI(res, entryID);
394 return false;
395 }
396 if (pos != indexStr.size())
397 {
398 messages::resourceMissingAtURI(res, entryID);
399 return false;
400 }
401 }
402 // Timestamp has no index
403 std::size_t pos;
404 try
405 {
Ed Tanous39e77502019-03-04 17:35:53 -0800406 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700407 }
Ed Tanous271584a2019-07-09 16:24:22 -0700408 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700409 {
410 messages::resourceMissingAtURI(res, entryID);
411 return false;
412 }
Ed Tanous271584a2019-07-09 16:24:22 -0700413 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700414 {
415 messages::resourceMissingAtURI(res, entryID);
416 return false;
417 }
418 if (pos != tsStr.size())
419 {
420 messages::resourceMissingAtURI(res, entryID);
421 return false;
422 }
423 return true;
424}
425
Jason M. Bills95820182019-04-22 16:25:34 -0700426static bool
427 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
428{
429 static const std::filesystem::path redfishLogDir = "/var/log";
430 static const std::string redfishLogFilename = "redfish";
431
432 // Loop through the directory looking for redfish log files
433 for (const std::filesystem::directory_entry &dirEnt :
434 std::filesystem::directory_iterator(redfishLogDir))
435 {
436 // If we find a redfish log file, save the path
437 std::string filename = dirEnt.path().filename();
438 if (boost::starts_with(filename, redfishLogFilename))
439 {
440 redfishLogFiles.emplace_back(redfishLogDir / filename);
441 }
442 }
443 // As the log files rotate, they are appended with a ".#" that is higher for
444 // the older logs. Since we don't expect more than 10 log files, we
445 // can just sort the list to get them in order from newest to oldest
446 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
447
448 return !redfishLogFiles.empty();
449}
450
Johnathan Mantey043a0532020-03-10 17:15:28 -0700451static void ParseCrashdumpParameters(
452 const std::vector<std::pair<std::string, VariantType>> &params,
453 std::string &filename, std::string &timestamp, std::string &logfile)
454{
455 for (auto property : params)
456 {
457 if (property.first == "Timestamp")
458 {
459 const std::string *value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500460 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700461 if (value != nullptr)
462 {
463 timestamp = *value;
464 }
465 }
466 else if (property.first == "Filename")
467 {
468 const std::string *value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500469 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700470 if (value != nullptr)
471 {
472 filename = *value;
473 }
474 }
475 else if (property.first == "Log")
476 {
477 const std::string *value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500478 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700479 if (value != nullptr)
480 {
481 logfile = *value;
482 }
483 }
484 }
485}
486
ZhikuiRena3316fc2020-01-29 14:58:08 -0800487constexpr char const *postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800488class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700489{
490 public:
491 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800492 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800493 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800494 {
495 entityPrivileges = {
496 {boost::beast::http::verb::get, {{"Login"}}},
497 {boost::beast::http::verb::head, {{"Login"}}},
498 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
499 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
500 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
501 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
502 }
503
504 private:
505 /**
506 * Functions triggers appropriate requests on DBus
507 */
508 void doGet(crow::Response &res, const crow::Request &req,
509 const std::vector<std::string> &params) override
510 {
511 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800512 // Collections don't include the static data added by SubRoute because
513 // it has a duplicate entry for members
514 asyncResp->res.jsonValue["@odata.type"] =
515 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800516 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800517 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800518 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
519 asyncResp->res.jsonValue["Description"] =
520 "Collection of LogServices for this Computer System";
521 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
522 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800523 logServiceArray.push_back(
524 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600525#ifdef BMCWEB_ENABLE_REDFISH_SYSTEMDUMP_LOG
526 logServiceArray.push_back(
527 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/System"}});
528#endif
529
Jason M. Billsd53dd412019-02-12 17:16:22 -0800530#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
531 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500532 {{"@odata.id",
533 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800534#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800535 asyncResp->res.jsonValue["Members@odata.count"] =
536 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800537
538 crow::connections::systemBus->async_method_call(
539 [asyncResp](const boost::system::error_code ec,
540 const std::vector<std::string> &subtreePath) {
541 if (ec)
542 {
543 BMCWEB_LOG_ERROR << ec;
544 return;
545 }
546
547 for (auto &pathStr : subtreePath)
548 {
549 if (pathStr.find("PostCode") != std::string::npos)
550 {
551 nlohmann::json &logServiceArray =
552 asyncResp->res.jsonValue["Members"];
553 logServiceArray.push_back(
554 {{"@odata.id", "/redfish/v1/Systems/system/"
555 "LogServices/PostCodes"}});
556 asyncResp->res.jsonValue["Members@odata.count"] =
557 logServiceArray.size();
558 return;
559 }
560 }
561 },
562 "xyz.openbmc_project.ObjectMapper",
563 "/xyz/openbmc_project/object_mapper",
564 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
565 std::array<const char *, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800566 }
567};
568
569class EventLogService : public Node
570{
571 public:
572 template <typename CrowApp>
573 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800574 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800575 {
576 entityPrivileges = {
577 {boost::beast::http::verb::get, {{"Login"}}},
578 {boost::beast::http::verb::head, {{"Login"}}},
579 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
580 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
581 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
582 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
583 }
584
585 private:
586 void doGet(crow::Response &res, const crow::Request &req,
587 const std::vector<std::string> &params) override
588 {
589 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
590
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800591 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800592 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800593 asyncResp->res.jsonValue["@odata.type"] =
594 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800595 asyncResp->res.jsonValue["Name"] = "Event Log Service";
596 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -0500597 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800598 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
599 asyncResp->res.jsonValue["Entries"] = {
600 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800601 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500602 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
603
604 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
605 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700606 }
607};
608
Tim Lee1f56a3a2019-10-09 10:17:57 +0800609class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700610{
611 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800612 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700613 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
614 "LogService.ClearLog/")
615 {
616 entityPrivileges = {
617 {boost::beast::http::verb::get, {{"Login"}}},
618 {boost::beast::http::verb::head, {{"Login"}}},
619 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
620 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
621 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
622 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
623 }
624
625 private:
626 void doPost(crow::Response &res, const crow::Request &req,
627 const std::vector<std::string> &params) override
628 {
629 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
630
631 // Clear the EventLog by deleting the log files
632 std::vector<std::filesystem::path> redfishLogFiles;
633 if (getRedfishLogFiles(redfishLogFiles))
634 {
635 for (const std::filesystem::path &file : redfishLogFiles)
636 {
637 std::error_code ec;
638 std::filesystem::remove(file, ec);
639 }
640 }
641
642 // Reload rsyslog so it knows to start new log files
643 crow::connections::systemBus->async_method_call(
644 [asyncResp](const boost::system::error_code ec) {
645 if (ec)
646 {
647 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
648 messages::internalError(asyncResp->res);
649 return;
650 }
651
652 messages::success(asyncResp->res);
653 },
654 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
655 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
656 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800657 }
658};
659
Jason M. Bills95820182019-04-22 16:25:34 -0700660static int fillEventLogEntryJson(const std::string &logEntryID,
661 const std::string logEntry,
662 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800663{
Jason M. Bills95820182019-04-22 16:25:34 -0700664 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700665 // First get the Timestamp
666 size_t space = logEntry.find_first_of(" ");
667 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700668 {
669 return 1;
670 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700671 std::string timestamp = logEntry.substr(0, space);
672 // Then get the log contents
673 size_t entryStart = logEntry.find_first_not_of(" ", space);
674 if (entryStart == std::string::npos)
675 {
676 return 1;
677 }
678 std::string_view entry(logEntry);
679 entry.remove_prefix(entryStart);
680 // Use split to separate the entry into its fields
681 std::vector<std::string> logEntryFields;
682 boost::split(logEntryFields, entry, boost::is_any_of(","),
683 boost::token_compress_on);
684 // We need at least a MessageId to be valid
685 if (logEntryFields.size() < 1)
686 {
687 return 1;
688 }
689 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700690
Jason M. Bills4851d452019-03-28 11:27:48 -0700691 // Get the Message from the MessageRegistry
692 const message_registries::Message *message =
693 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800694
Jason M. Bills4851d452019-03-28 11:27:48 -0700695 std::string msg;
696 std::string severity;
697 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800698 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700699 msg = message->message;
700 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800701 }
702
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700703 // Get the MessageArgs from the log if there are any
704 boost::beast::span<std::string> messageArgs;
705 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700706 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700707 std::string &messageArgsStart = logEntryFields[1];
708 // If the first string is empty, assume there are no MessageArgs
709 std::size_t messageArgsSize = 0;
710 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700711 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700712 messageArgsSize = logEntryFields.size() - 1;
713 }
714
715 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
716
717 // Fill the MessageArgs into the Message
718 int i = 0;
719 for (const std::string &messageArg : messageArgs)
720 {
721 std::string argStr = "%" + std::to_string(++i);
722 size_t argPos = msg.find(argStr);
723 if (argPos != std::string::npos)
724 {
725 msg.replace(argPos, argStr.length(), messageArg);
726 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700727 }
728 }
729
Jason M. Bills95820182019-04-22 16:25:34 -0700730 // Get the Created time from the timestamp. The log timestamp is in RFC3339
731 // format which matches the Redfish format except for the fractional seconds
732 // between the '.' and the '+', so just remove them.
733 std::size_t dot = timestamp.find_first_of(".");
734 std::size_t plus = timestamp.find_first_of("+");
735 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800736 {
Jason M. Bills95820182019-04-22 16:25:34 -0700737 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800738 }
739
740 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700741 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700742 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800743 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700744 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700745 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800746 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700747 {"Id", logEntryID},
748 {"Message", std::move(msg)},
749 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800750 {"MessageArgs", std::move(messageArgs)},
751 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700752 {"Severity", std::move(severity)},
753 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800754 return 0;
755}
756
Anthony Wilson27062602019-04-22 02:10:09 -0500757class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800758{
759 public:
760 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500761 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800762 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800763 {
764 entityPrivileges = {
765 {boost::beast::http::verb::get, {{"Login"}}},
766 {boost::beast::http::verb::head, {{"Login"}}},
767 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
768 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
769 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
770 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
771 }
772
773 private:
774 void doGet(crow::Response &res, const crow::Request &req,
775 const std::vector<std::string> &params) override
776 {
777 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700778 uint64_t skip = 0;
779 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800780 if (!getSkipParam(asyncResp->res, req, skip))
781 {
782 return;
783 }
784 if (!getTopParam(asyncResp->res, req, top))
785 {
786 return;
787 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800788 // Collections don't include the static data added by SubRoute because
789 // it has a duplicate entry for members
790 asyncResp->res.jsonValue["@odata.type"] =
791 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800792 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800793 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800794 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
795 asyncResp->res.jsonValue["Description"] =
796 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700797
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800798 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
799 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700800 // Go through the log files and create a unique ID for each entry
801 std::vector<std::filesystem::path> redfishLogFiles;
802 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000803 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700804 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700805
806 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700807 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
808 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800809 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700810 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700811 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800812 {
813 continue;
814 }
815
Jason M. Billse85d6b12019-07-29 17:01:15 -0700816 // Reset the unique ID on the first entry
817 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700818 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800819 {
Jason M. Bills95820182019-04-22 16:25:34 -0700820 entryCount++;
821 // Handle paging using skip (number of entries to skip from the
822 // start) and top (number of entries to display)
823 if (entryCount <= skip || entryCount > skip + top)
824 {
825 continue;
826 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800827
Jason M. Bills95820182019-04-22 16:25:34 -0700828 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700829 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700830 {
831 continue;
832 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800833
Jason M. Billse85d6b12019-07-29 17:01:15 -0700834 if (firstEntry)
835 {
836 firstEntry = false;
837 }
838
Jason M. Bills95820182019-04-22 16:25:34 -0700839 logEntryArray.push_back({});
840 nlohmann::json &bmcLogEntry = logEntryArray.back();
841 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
842 {
843 messages::internalError(asyncResp->res);
844 return;
845 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800846 }
847 }
848 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
849 if (skip + top < entryCount)
850 {
851 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700852 "/redfish/v1/Systems/system/LogServices/EventLog/"
853 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800854 std::to_string(skip + top);
855 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500856 }
857};
858
Jason M. Bills897967d2019-07-29 17:05:30 -0700859class JournalEventLogEntry : public Node
860{
861 public:
862 JournalEventLogEntry(CrowApp &app) :
863 Node(app,
864 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
865 std::string())
866 {
867 entityPrivileges = {
868 {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"}}}};
874 }
875
876 private:
877 void doGet(crow::Response &res, const crow::Request &req,
878 const std::vector<std::string> &params) override
879 {
880 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
881 if (params.size() != 1)
882 {
883 messages::internalError(asyncResp->res);
884 return;
885 }
886 const std::string &targetID = params[0];
887
888 // Go through the log files and check the unique ID for each entry to
889 // find the target entry
890 std::vector<std::filesystem::path> redfishLogFiles;
891 getRedfishLogFiles(redfishLogFiles);
892 std::string logEntry;
893
894 // Oldest logs are in the last file, so start there and loop backwards
895 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
896 it++)
897 {
898 std::ifstream logStream(*it);
899 if (!logStream.is_open())
900 {
901 continue;
902 }
903
904 // Reset the unique ID on the first entry
905 bool firstEntry = true;
906 while (std::getline(logStream, logEntry))
907 {
908 std::string idStr;
909 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
910 {
911 continue;
912 }
913
914 if (firstEntry)
915 {
916 firstEntry = false;
917 }
918
919 if (idStr == targetID)
920 {
921 if (fillEventLogEntryJson(idStr, logEntry,
922 asyncResp->res.jsonValue) != 0)
923 {
924 messages::internalError(asyncResp->res);
925 return;
926 }
927 return;
928 }
929 }
930 }
931 // Requested ID was not found
932 messages::resourceMissingAtURI(asyncResp->res, targetID);
933 }
934};
935
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500936class DBusEventLogEntryCollection : public Node
937{
938 public:
939 template <typename CrowApp>
940 DBusEventLogEntryCollection(CrowApp &app) :
941 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
942 {
943 entityPrivileges = {
944 {boost::beast::http::verb::get, {{"Login"}}},
945 {boost::beast::http::verb::head, {{"Login"}}},
946 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
947 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
948 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
949 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
950 }
951
952 private:
953 void doGet(crow::Response &res, const crow::Request &req,
954 const std::vector<std::string> &params) override
955 {
956 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
957
958 // Collections don't include the static data added by SubRoute because
959 // it has a duplicate entry for members
960 asyncResp->res.jsonValue["@odata.type"] =
961 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500962 asyncResp->res.jsonValue["@odata.id"] =
963 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
964 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
965 asyncResp->res.jsonValue["Description"] =
966 "Collection of System Event Log Entries";
967
Andrew Geisslercb92c032018-08-17 07:56:14 -0700968 // DBus implementation of EventLog/Entries
969 // Make call to Logging Service to find all log entry objects
970 crow::connections::systemBus->async_method_call(
971 [asyncResp](const boost::system::error_code ec,
972 GetManagedObjectsType &resp) {
973 if (ec)
974 {
975 // TODO Handle for specific error code
976 BMCWEB_LOG_ERROR
977 << "getLogEntriesIfaceData resp_handler got error "
978 << ec;
979 messages::internalError(asyncResp->res);
980 return;
981 }
982 nlohmann::json &entriesArray =
983 asyncResp->res.jsonValue["Members"];
984 entriesArray = nlohmann::json::array();
985 for (auto &objectPath : resp)
986 {
987 for (auto &interfaceMap : objectPath.second)
988 {
989 if (interfaceMap.first !=
990 "xyz.openbmc_project.Logging.Entry")
991 {
992 BMCWEB_LOG_DEBUG << "Bailing early on "
993 << interfaceMap.first;
994 continue;
995 }
996 entriesArray.push_back({});
997 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -0700998 uint32_t *id = nullptr;
999 std::time_t timestamp{};
1000 std::string *severity = nullptr;
1001 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001002 for (auto &propertyMap : interfaceMap.second)
1003 {
1004 if (propertyMap.first == "Id")
1005 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001006 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001007 if (id == nullptr)
1008 {
1009 messages::propertyMissing(asyncResp->res,
1010 "Id");
1011 }
1012 }
1013 else if (propertyMap.first == "Timestamp")
1014 {
1015 const uint64_t *millisTimeStamp =
1016 std::get_if<uint64_t>(&propertyMap.second);
1017 if (millisTimeStamp == nullptr)
1018 {
1019 messages::propertyMissing(asyncResp->res,
1020 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001021 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001022 }
1023 // Retrieve Created property with format:
1024 // yyyy-mm-ddThh:mm:ss
1025 std::chrono::milliseconds chronoTimeStamp(
1026 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001027 timestamp = std::chrono::duration_cast<
1028 std::chrono::duration<int>>(
1029 chronoTimeStamp)
1030 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001031 }
1032 else if (propertyMap.first == "Severity")
1033 {
1034 severity = std::get_if<std::string>(
1035 &propertyMap.second);
1036 if (severity == nullptr)
1037 {
1038 messages::propertyMissing(asyncResp->res,
1039 "Severity");
1040 }
1041 }
1042 else if (propertyMap.first == "Message")
1043 {
1044 message = std::get_if<std::string>(
1045 &propertyMap.second);
1046 if (message == nullptr)
1047 {
1048 messages::propertyMissing(asyncResp->res,
1049 "Message");
1050 }
1051 }
1052 }
1053 thisEntry = {
1054 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001055 {"@odata.id",
1056 "/redfish/v1/Systems/system/LogServices/EventLog/"
1057 "Entries/" +
1058 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001059 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001060 {"Id", std::to_string(*id)},
1061 {"Message", *message},
1062 {"EntryType", "Event"},
1063 {"Severity",
1064 translateSeverityDbusToRedfish(*severity)},
1065 {"Created", crow::utility::getDateTime(timestamp)}};
1066 }
1067 }
1068 std::sort(entriesArray.begin(), entriesArray.end(),
1069 [](const nlohmann::json &left,
1070 const nlohmann::json &right) {
1071 return (left["Id"] <= right["Id"]);
1072 });
1073 asyncResp->res.jsonValue["Members@odata.count"] =
1074 entriesArray.size();
1075 },
1076 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1077 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001078 }
1079};
1080
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001081class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001082{
1083 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001084 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001085 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001086 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1087 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001088 {
1089 entityPrivileges = {
1090 {boost::beast::http::verb::get, {{"Login"}}},
1091 {boost::beast::http::verb::head, {{"Login"}}},
1092 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1093 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1094 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1095 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1096 }
1097
1098 private:
1099 void doGet(crow::Response &res, const crow::Request &req,
1100 const std::vector<std::string> &params) override
1101 {
1102 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001103 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001104 {
1105 messages::internalError(asyncResp->res);
1106 return;
1107 }
Ed Tanous029573d2019-02-01 10:57:49 -08001108 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001109
Andrew Geisslercb92c032018-08-17 07:56:14 -07001110 // DBus implementation of EventLog/Entries
1111 // Make call to Logging Service to find all log entry objects
1112 crow::connections::systemBus->async_method_call(
1113 [asyncResp, entryID](const boost::system::error_code ec,
1114 GetManagedPropertyType &resp) {
1115 if (ec)
1116 {
1117 BMCWEB_LOG_ERROR
1118 << "EventLogEntry (DBus) resp_handler got error " << ec;
1119 messages::internalError(asyncResp->res);
1120 return;
1121 }
Ed Tanous66664f22019-10-11 13:05:49 -07001122 uint32_t *id = nullptr;
1123 std::time_t timestamp{};
1124 std::string *severity = nullptr;
1125 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001126 for (auto &propertyMap : resp)
1127 {
1128 if (propertyMap.first == "Id")
1129 {
1130 id = std::get_if<uint32_t>(&propertyMap.second);
1131 if (id == nullptr)
1132 {
1133 messages::propertyMissing(asyncResp->res, "Id");
1134 }
1135 }
1136 else if (propertyMap.first == "Timestamp")
1137 {
1138 const uint64_t *millisTimeStamp =
1139 std::get_if<uint64_t>(&propertyMap.second);
1140 if (millisTimeStamp == nullptr)
1141 {
1142 messages::propertyMissing(asyncResp->res,
1143 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001144 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001145 }
1146 // Retrieve Created property with format:
1147 // yyyy-mm-ddThh:mm:ss
1148 std::chrono::milliseconds chronoTimeStamp(
1149 *millisTimeStamp);
1150 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001151 std::chrono::duration_cast<
1152 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001153 .count();
1154 }
1155 else if (propertyMap.first == "Severity")
1156 {
1157 severity =
1158 std::get_if<std::string>(&propertyMap.second);
1159 if (severity == nullptr)
1160 {
1161 messages::propertyMissing(asyncResp->res,
1162 "Severity");
1163 }
1164 }
1165 else if (propertyMap.first == "Message")
1166 {
1167 message = std::get_if<std::string>(&propertyMap.second);
1168 if (message == nullptr)
1169 {
1170 messages::propertyMissing(asyncResp->res,
1171 "Message");
1172 }
1173 }
1174 }
Ed Tanous271584a2019-07-09 16:24:22 -07001175 if (id == nullptr || message == nullptr || severity == nullptr)
1176 {
1177 return;
1178 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001179 asyncResp->res.jsonValue = {
1180 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001181 {"@odata.id",
1182 "/redfish/v1/Systems/system/LogServices/EventLog/"
1183 "Entries/" +
1184 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001185 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001186 {"Id", std::to_string(*id)},
1187 {"Message", *message},
1188 {"EntryType", "Event"},
1189 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001190 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001191 },
1192 "xyz.openbmc_project.Logging",
1193 "/xyz/openbmc_project/logging/entry/" + entryID,
1194 "org.freedesktop.DBus.Properties", "GetAll",
1195 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001196 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001197
1198 void doDelete(crow::Response &res, const crow::Request &req,
1199 const std::vector<std::string> &params) override
1200 {
1201
1202 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1203
1204 auto asyncResp = std::make_shared<AsyncResp>(res);
1205
1206 if (params.size() != 1)
1207 {
1208 messages::internalError(asyncResp->res);
1209 return;
1210 }
1211 std::string entryID = params[0];
1212
1213 dbus::utility::escapePathForDbus(entryID);
1214
1215 // Process response from Logging service.
1216 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1217 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1218 if (ec)
1219 {
1220 // TODO Handle for specific error code
1221 BMCWEB_LOG_ERROR
1222 << "EventLogEntry (DBus) doDelete respHandler got error "
1223 << ec;
1224 asyncResp->res.result(
1225 boost::beast::http::status::internal_server_error);
1226 return;
1227 }
1228
1229 asyncResp->res.result(boost::beast::http::status::ok);
1230 };
1231
1232 // Make call to Logging service to request Delete Log
1233 crow::connections::systemBus->async_method_call(
1234 respHandler, "xyz.openbmc_project.Logging",
1235 "/xyz/openbmc_project/logging/entry/" + entryID,
1236 "xyz.openbmc_project.Object.Delete", "Delete");
1237 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001238};
1239
1240class BMCLogServiceCollection : public Node
1241{
1242 public:
1243 template <typename CrowApp>
1244 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001245 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001246 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001247 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001248 {boost::beast::http::verb::get, {{"Login"}}},
1249 {boost::beast::http::verb::head, {{"Login"}}},
1250 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1251 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1252 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1253 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001254 }
1255
1256 private:
1257 /**
1258 * Functions triggers appropriate requests on DBus
1259 */
1260 void doGet(crow::Response &res, const crow::Request &req,
1261 const std::vector<std::string> &params) override
1262 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001263 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001264 // Collections don't include the static data added by SubRoute because
1265 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001266 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001267 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001268 asyncResp->res.jsonValue["@odata.id"] =
1269 "/redfish/v1/Managers/bmc/LogServices";
1270 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1271 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001272 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001273 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1274 logServiceArray = nlohmann::json::array();
1275#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1276 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001277 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001278#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001279 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001280 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001281 }
1282};
1283
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001284class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001285{
1286 public:
1287 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001288 BMCJournalLogService(CrowApp &app) :
1289 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001290 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001291 entityPrivileges = {
1292 {boost::beast::http::verb::get, {{"Login"}}},
1293 {boost::beast::http::verb::head, {{"Login"}}},
1294 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1295 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1296 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1297 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1298 }
1299
1300 private:
1301 void doGet(crow::Response &res, const crow::Request &req,
1302 const std::vector<std::string> &params) override
1303 {
1304 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001305 asyncResp->res.jsonValue["@odata.type"] =
1306 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001307 asyncResp->res.jsonValue["@odata.id"] =
1308 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001309 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1310 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1311 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001312 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001313 asyncResp->res.jsonValue["Entries"] = {
1314 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001315 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001316 }
1317};
1318
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001319static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1320 sd_journal *journal,
1321 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001322{
1323 // Get the Log Entry contents
1324 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001325
Ed Tanous39e77502019-03-04 17:35:53 -08001326 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001327 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001328 if (ret < 0)
1329 {
1330 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1331 return 1;
1332 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001333
1334 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001335 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001336 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001337 if (ret < 0)
1338 {
1339 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001340 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001341
1342 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001343 std::string entryTimeStr;
1344 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001345 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001346 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001347 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001348
1349 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001350 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001351 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001352 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1353 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001354 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001355 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001356 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001357 {"EntryType", "Oem"},
1358 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001359 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001360 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001361 {"Created", std::move(entryTimeStr)}};
1362 return 0;
1363}
1364
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001365class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001366{
1367 public:
1368 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001369 BMCJournalLogEntryCollection(CrowApp &app) :
1370 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001371 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001372 entityPrivileges = {
1373 {boost::beast::http::verb::get, {{"Login"}}},
1374 {boost::beast::http::verb::head, {{"Login"}}},
1375 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1376 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1377 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1378 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1379 }
1380
1381 private:
1382 void doGet(crow::Response &res, const crow::Request &req,
1383 const std::vector<std::string> &params) override
1384 {
1385 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001386 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001387 uint64_t skip = 0;
1388 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001389 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001390 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001391 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001392 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001393 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001394 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001395 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001396 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001397 // Collections don't include the static data added by SubRoute because
1398 // it has a duplicate entry for members
1399 asyncResp->res.jsonValue["@odata.type"] =
1400 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001401 asyncResp->res.jsonValue["@odata.id"] =
1402 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001403 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001404 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001405 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1406 asyncResp->res.jsonValue["Description"] =
1407 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001408 asyncResp->res.jsonValue["@odata.id"] =
1409 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001410 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1411 logEntryArray = nlohmann::json::array();
1412
1413 // Go through the journal and use the timestamp to create a unique ID
1414 // for each entry
1415 sd_journal *journalTmp = nullptr;
1416 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1417 if (ret < 0)
1418 {
1419 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001420 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001421 return;
1422 }
1423 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1424 journalTmp, sd_journal_close);
1425 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001426 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001427 // Reset the unique ID on the first entry
1428 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001429 SD_JOURNAL_FOREACH(journal.get())
1430 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001431 entryCount++;
1432 // Handle paging using skip (number of entries to skip from the
1433 // start) and top (number of entries to display)
1434 if (entryCount <= skip || entryCount > skip + top)
1435 {
1436 continue;
1437 }
1438
Jason M. Bills16428a12018-11-02 12:42:29 -07001439 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001440 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001441 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001442 continue;
1443 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001444
Jason M. Billse85d6b12019-07-29 17:01:15 -07001445 if (firstEntry)
1446 {
1447 firstEntry = false;
1448 }
1449
Jason M. Billse1f26342018-07-18 12:12:00 -07001450 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001451 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1452 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1453 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001454 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001455 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001456 return;
1457 }
1458 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001459 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1460 if (skip + top < entryCount)
1461 {
1462 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001463 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001464 std::to_string(skip + top);
1465 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001466 }
1467};
1468
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001469class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001470{
1471 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001472 BMCJournalLogEntry(CrowApp &app) :
1473 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001474 std::string())
1475 {
1476 entityPrivileges = {
1477 {boost::beast::http::verb::get, {{"Login"}}},
1478 {boost::beast::http::verb::head, {{"Login"}}},
1479 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1480 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1481 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1482 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1483 }
1484
1485 private:
1486 void doGet(crow::Response &res, const crow::Request &req,
1487 const std::vector<std::string> &params) override
1488 {
1489 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1490 if (params.size() != 1)
1491 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001492 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001493 return;
1494 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001495 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001496 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001497 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001498 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001499 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001500 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001501 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001502 }
1503
1504 sd_journal *journalTmp = nullptr;
1505 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1506 if (ret < 0)
1507 {
1508 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001509 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001510 return;
1511 }
1512 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1513 journalTmp, sd_journal_close);
1514 journalTmp = nullptr;
1515 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001516 // tracking the unique ID
1517 std::string idStr;
1518 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001519 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001520 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001521 {
1522 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001523 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1524 {
1525 messages::internalError(asyncResp->res);
1526 return;
1527 }
1528 if (firstEntry)
1529 {
1530 firstEntry = false;
1531 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001532 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001533 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001534 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001535 {
1536 messages::resourceMissingAtURI(asyncResp->res, entryID);
1537 return;
1538 }
1539
1540 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1541 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001542 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001543 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001544 return;
1545 }
1546 }
1547};
1548
raviteja-bc9bb6862020-02-03 11:53:32 -06001549class SystemDumpService : public Node
1550{
1551 public:
1552 template <typename CrowApp>
1553 SystemDumpService(CrowApp &app) :
1554 Node(app, "/redfish/v1/Systems/system/LogServices/System/")
1555 {
1556 entityPrivileges = {
1557 {boost::beast::http::verb::get, {{"Login"}}},
1558 {boost::beast::http::verb::head, {{"Login"}}},
1559 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1560 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1561 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1562 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1563 }
1564
1565 private:
1566 void doGet(crow::Response &res, const crow::Request &req,
1567 const std::vector<std::string> &params) override
1568 {
1569 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1570
1571 asyncResp->res.jsonValue["@odata.id"] =
1572 "/redfish/v1/Systems/system/LogServices/System";
1573 asyncResp->res.jsonValue["@odata.type"] =
1574 "#LogService.v1_1_0.LogService";
1575 asyncResp->res.jsonValue["Name"] = "Dump Log Service";
1576 asyncResp->res.jsonValue["Description"] = "System Dump Log Service";
1577 asyncResp->res.jsonValue["Id"] = "System";
1578 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1579 asyncResp->res.jsonValue["LogEntryTypes"] = "Dump";
1580 asyncResp->res.jsonValue["Oem"]["DumpType"] = "System";
1581
1582 asyncResp->res.jsonValue["Entries"] = {
1583 {"@odata.id",
1584 "/redfish/v1/Systems/system/LogServices/System/Entries"}};
1585 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1586 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1587 "Actions/LogService.ClearLog"}};
1588 asyncResp->res.jsonValue["Actions"]["#LogService.CreateLog"] = {
1589 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1590 "Actions/LogService.CreateLog"}};
1591 }
1592};
1593
1594class SystemDumpEntryCollection : public Node
1595{
1596 public:
1597 template <typename CrowApp>
1598 SystemDumpEntryCollection(CrowApp &app) :
1599 Node(app, "/redfish/v1/Systems/system/LogServices/System/Entries/")
1600 {
1601 entityPrivileges = {
1602 {boost::beast::http::verb::get, {{"Login"}}},
1603 {boost::beast::http::verb::head, {{"Login"}}},
1604 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1605 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1606 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1607 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1608 }
1609
1610 private:
1611 /**
1612 * Functions triggers appropriate requests on DBus
1613 */
1614 void doGet(crow::Response &res, const crow::Request &req,
1615 const std::vector<std::string> &params) override
1616 {
1617 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1618
1619 asyncResp->res.jsonValue["@odata.type"] =
1620 "#LogEntryCollection.LogEntryCollection";
1621 asyncResp->res.jsonValue["@odata.id"] =
1622 "/redfish/v1/Systems/system/LogServices/System/Entries";
1623 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
1624 asyncResp->res.jsonValue["Description"] =
1625 "Collection of System Dump Entries";
1626
1627 crow::connections::systemBus->async_method_call(
1628 [asyncResp](const boost::system::error_code ec,
1629 const crow::openbmc_mapper::GetSubTreeType &resp) {
1630 if (ec)
1631 {
1632 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1633 messages::internalError(asyncResp->res);
1634 return;
1635 }
1636
1637 nlohmann::json &logArray = asyncResp->res.jsonValue["Members"];
1638 logArray = nlohmann::json::array();
1639 for (auto &object : resp)
1640 {
1641 const std::string &path =
1642 static_cast<const std::string &>(object.first);
1643 std::size_t lastPos = path.rfind("/");
1644 if (lastPos == std::string::npos)
1645 {
1646 continue;
1647 }
1648 std::string logID = path.substr(lastPos + 1);
1649 logArray.push_back(
1650 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1651 "System/Entries/" +
1652 logID}});
1653 }
1654 asyncResp->res.jsonValue["Members@odata.count"] =
1655 logArray.size();
1656 },
1657 "xyz.openbmc_project.ObjectMapper",
1658 "/xyz/openbmc_project/object_mapper",
1659 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1660 "/xyz/openbmc_project/dump", 0,
1661 std::array<const char *, 1>{
1662 "xyz.openbmc_project.Dump.Entry.System"});
1663 }
1664};
1665
1666class SystemDumpEntry : public Node
1667{
1668 public:
1669 SystemDumpEntry(CrowApp &app) :
1670 Node(app,
1671 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/",
1672 std::string())
1673 {
1674 entityPrivileges = {
1675 {boost::beast::http::verb::get, {{"Login"}}},
1676 {boost::beast::http::verb::head, {{"Login"}}},
1677 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1678 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1679 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1680 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1681 }
1682
1683 private:
1684 void doGet(crow::Response &res, const crow::Request &req,
1685 const std::vector<std::string> &params) override
1686 {
1687 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1688 if (params.size() != 1)
1689 {
1690 messages::internalError(asyncResp->res);
1691 return;
1692 }
1693 const std::string &entryID = params[0];
1694 crow::connections::systemBus->async_method_call(
1695 [asyncResp, entryID](const boost::system::error_code ec,
1696 GetManagedObjectsType &resp) {
1697 if (ec)
1698 {
1699 BMCWEB_LOG_ERROR
1700 << "SystemDumpEntry resp_handler got error " << ec;
1701 messages::internalError(asyncResp->res);
1702 return;
1703 }
1704
1705 for (auto &objectPath : resp)
1706 {
1707 if (objectPath.first.str.find(
1708 "/xyz/openbmc_project/dump/entry/" + entryID) ==
1709 std::string::npos)
1710 {
1711 continue;
1712 }
1713
1714 bool foundSystemDumpEntry = false;
1715 for (auto &interfaceMap : objectPath.second)
1716 {
1717 if (interfaceMap.first ==
1718 "xyz.openbmc_project.Dump.Entry.System")
1719 {
1720 foundSystemDumpEntry = true;
1721 break;
1722 }
1723 }
1724 if (foundSystemDumpEntry == false)
1725 {
1726 BMCWEB_LOG_DEBUG << "Can't find System Dump Entry";
1727 messages::internalError(asyncResp->res);
1728 return;
1729 }
1730
1731 std::string timestamp{};
1732 uint64_t size = 0;
1733
1734 for (auto &interfaceMap : objectPath.second)
1735 {
1736 if (interfaceMap.first ==
1737 "xyz.openbmc_project.Dump.Entry")
1738 {
1739 for (auto &propertyMap : interfaceMap.second)
1740 {
1741 if (propertyMap.first == "Size")
1742 {
1743 auto sizePtr = std::get_if<uint64_t>(
1744 &propertyMap.second);
1745 if (sizePtr == nullptr)
1746 {
1747 messages::propertyMissing(
1748 asyncResp->res, "Size");
1749 break;
1750 }
1751 size = *sizePtr;
1752 break;
1753 }
1754 }
1755 }
1756 else if (interfaceMap.first ==
1757 "xyz.openbmc_project.Time.EpochTime")
1758 {
1759 for (auto &propertyMap : interfaceMap.second)
1760 {
1761 if (propertyMap.first == "Elapsed")
1762 {
1763 const uint64_t *usecsTimeStamp =
1764 std::get_if<uint64_t>(
1765 &propertyMap.second);
1766 if (usecsTimeStamp == nullptr)
1767 {
1768 messages::propertyMissing(
1769 asyncResp->res, "Elapsed");
1770 break;
1771 }
1772 getTimestampStr(*usecsTimeStamp, timestamp);
1773 break;
1774 }
1775 }
1776 }
1777 }
1778 asyncResp->res.jsonValue = {
1779 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1780 {"@odata.id",
1781 "/redfish/v1/Systems/system/LogServices/System/"
1782 "Entries/" +
1783 entryID},
1784 {"Name", "System Dump Entry"},
1785 {"Id", entryID},
1786 {"SizeInB", size},
1787 {"EntryType", "Dump"},
1788 {"EntryCode", "User generated dump"},
1789 {"Created", timestamp}};
1790
1791 asyncResp->res
1792 .jsonValue["Actions"]["#LogEntry.DownloadLog"] = {
1793 {"target",
1794 "/redfish/v1/Systems/system/LogServices/System/"
1795 "Entries/" +
1796 entryID + "/Actions/LogEntry.DownloadLog"}};
1797 }
1798 },
1799 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
1800 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1801 }
1802
1803 void doDelete(crow::Response &res, const crow::Request &req,
1804 const std::vector<std::string> &params) override
1805 {
1806 BMCWEB_LOG_DEBUG << "Do delete single dump entry";
1807
1808 auto asyncResp = std::make_shared<AsyncResp>(res);
1809
1810 if (params.size() != 1)
1811 {
1812 messages::internalError(asyncResp->res);
1813 return;
1814 }
1815 std::string entryID = params[0];
1816
1817 crow::connections::systemBus->async_method_call(
1818 [asyncResp,
1819 entryID](const boost::system::error_code ec,
1820 const crow::openbmc_mapper::GetSubTreeType &resp) {
1821 if (ec)
1822 {
1823 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1824 messages::internalError(asyncResp->res);
1825 return;
1826 }
1827
1828 for (auto &object : resp)
1829 {
1830 const std::string &path =
1831 static_cast<const std::string &>(object.first);
1832
1833 std::size_t pos = path.rfind(
1834 "/xyz/openbmc_project/dump/entry/" + entryID);
1835 if (pos != std::string::npos)
1836 {
1837 deleteSystemDumpEntry(asyncResp->res, entryID);
1838 return;
1839 }
1840 }
1841 },
1842 "xyz.openbmc_project.ObjectMapper",
1843 "/xyz/openbmc_project/object_mapper",
1844 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1845 "/xyz/openbmc_project/dump", 0,
1846 std::array<const char *, 1>{
1847 "xyz.openbmc_project.Dump.Entry.System"});
1848 }
1849};
1850
raviteja-b06578432020-02-03 12:50:42 -06001851class SystemDumpEntryDownload : public Node
1852{
1853 public:
1854 SystemDumpEntryDownload(CrowApp &app) :
1855 Node(app,
1856 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/"
1857 "Actions/"
1858 "LogEntry.DownloadLog/",
1859 std::string())
1860 {
1861 entityPrivileges = {
1862 {boost::beast::http::verb::get, {{"Login"}}},
1863 {boost::beast::http::verb::head, {{"Login"}}},
1864 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1865 }
1866
1867 private:
1868 void doPost(crow::Response &res, const crow::Request &req,
1869 const std::vector<std::string> &params) override
1870 {
1871 if (params.size() != 1)
1872 {
1873 messages::internalError(res);
1874 return;
1875 }
1876 const std::string &entryID = params[0];
1877 crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID);
1878 }
1879};
1880
raviteja-b013487e2020-03-03 03:20:48 -06001881class SystemDumpClear : public Node
1882{
1883 public:
1884 SystemDumpClear(CrowApp &app) :
1885 Node(app, "/redfish/v1/Systems/system/LogServices/System/"
1886 "Actions/"
1887 "LogService.ClearLog/")
1888 {
1889 entityPrivileges = {
1890 {boost::beast::http::verb::get, {{"Login"}}},
1891 {boost::beast::http::verb::head, {{"Login"}}},
1892 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1893 }
1894
1895 private:
1896 void doPost(crow::Response &res, const crow::Request &req,
1897 const std::vector<std::string> &params) override
1898 {
1899
1900 auto asyncResp = std::make_shared<AsyncResp>(res);
1901 crow::connections::systemBus->async_method_call(
1902 [asyncResp](const boost::system::error_code ec,
1903 const std::vector<std::string> &dumpList) {
1904 if (ec)
1905 {
1906 messages::internalError(asyncResp->res);
1907 return;
1908 }
1909
1910 for (const std::string &objectPath : dumpList)
1911 {
1912 std::size_t pos = objectPath.rfind("/");
1913 if (pos != std::string::npos)
1914 {
1915 std::string logID = objectPath.substr(pos + 1);
1916 deleteSystemDumpEntry(asyncResp->res, logID);
1917 }
1918 }
1919 },
1920 "xyz.openbmc_project.ObjectMapper",
1921 "/xyz/openbmc_project/object_mapper",
1922 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1923 "/xyz/openbmc_project/dump", 0,
1924 std::array<const char *, 1>{
1925 "xyz.openbmc_project.Dump.Entry.System"});
1926 }
1927};
1928
Jason M. Bills424c4172019-03-21 13:50:33 -07001929class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001930{
1931 public:
1932 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001933 CrashdumpService(CrowApp &app) :
1934 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001935 {
AppaRao Puli39460282020-04-07 17:03:04 +05301936 // Note: Deviated from redfish privilege registry for GET & HEAD
1937 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001938 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301939 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1940 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001941 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1942 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1943 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1944 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001945 }
1946
1947 private:
1948 /**
1949 * Functions triggers appropriate requests on DBus
1950 */
1951 void doGet(crow::Response &res, const crow::Request &req,
1952 const std::vector<std::string> &params) override
1953 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001954 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001955 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001956 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001957 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001958 asyncResp->res.jsonValue["@odata.type"] =
1959 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001960 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1961 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1962 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001963 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1964 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001965 asyncResp->res.jsonValue["Entries"] = {
1966 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001967 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001968 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001969 {"#LogService.ClearLog",
1970 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1971 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001972 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001973 {{"#Crashdump.OnDemand",
1974 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1975 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001976
1977#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001978 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001979 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001980 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1981 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001982#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001983 }
1984};
1985
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001986class CrashdumpClear : public Node
1987{
1988 public:
1989 CrashdumpClear(CrowApp &app) :
1990 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1991 "LogService.ClearLog/")
1992 {
AppaRao Puli39460282020-04-07 17:03:04 +05301993 // Note: Deviated from redfish privilege registry for GET & HEAD
1994 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001995 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301996 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1997 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001998 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1999 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2000 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2001 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2002 }
2003
2004 private:
2005 void doPost(crow::Response &res, const crow::Request &req,
2006 const std::vector<std::string> &params) override
2007 {
2008 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2009
2010 crow::connections::systemBus->async_method_call(
2011 [asyncResp](const boost::system::error_code ec,
2012 const std::string &resp) {
2013 if (ec)
2014 {
2015 messages::internalError(asyncResp->res);
2016 return;
2017 }
2018 messages::success(asyncResp->res);
2019 },
2020 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2021 }
2022};
2023
Jason M. Billse855dd22019-10-08 11:37:48 -07002024static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
2025 const std::string &logID,
2026 nlohmann::json &logEntryJson)
2027{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002028 auto getStoredLogCallback =
2029 [asyncResp, logID, &logEntryJson](
2030 const boost::system::error_code ec,
2031 const std::vector<std::pair<std::string, VariantType>> &params) {
2032 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002033 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002034 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2035 if (ec.value() ==
2036 boost::system::linux_error::bad_request_descriptor)
2037 {
2038 messages::resourceNotFound(asyncResp->res, "LogEntry",
2039 logID);
2040 }
2041 else
2042 {
2043 messages::internalError(asyncResp->res);
2044 }
2045 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002046 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002047
Johnathan Mantey043a0532020-03-10 17:15:28 -07002048 std::string timestamp{};
2049 std::string filename{};
2050 std::string logfile{};
2051 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2052
2053 if (filename.empty() || timestamp.empty())
2054 {
2055 messages::resourceMissingAtURI(asyncResp->res, logID);
2056 return;
2057 }
2058
2059 std::string crashdumpURI =
2060 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2061 logID + "/" + filename;
2062 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2063 {"@odata.id", "/redfish/v1/Systems/system/"
2064 "LogServices/Crashdump/Entries/" +
2065 logID},
2066 {"Name", "CPU Crashdump"},
2067 {"Id", logID},
2068 {"EntryType", "Oem"},
2069 {"OemRecordFormat", "Crashdump URI"},
2070 {"Message", std::move(crashdumpURI)},
2071 {"Created", std::move(timestamp)}};
2072 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002073 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002074 std::move(getStoredLogCallback), crashdumpObject,
2075 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002076 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002077}
2078
Jason M. Bills424c4172019-03-21 13:50:33 -07002079class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002080{
2081 public:
2082 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07002083 CrashdumpEntryCollection(CrowApp &app) :
2084 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002085 {
AppaRao Puli39460282020-04-07 17:03:04 +05302086 // Note: Deviated from redfish privilege registry for GET & HEAD
2087 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002088 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302089 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2090 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002091 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2092 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2093 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2094 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002095 }
2096
2097 private:
2098 /**
2099 * Functions triggers appropriate requests on DBus
2100 */
2101 void doGet(crow::Response &res, const crow::Request &req,
2102 const std::vector<std::string> &params) override
2103 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002104 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002105 // Collections don't include the static data added by SubRoute because
2106 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002107 auto getLogEntriesCallback = [asyncResp](
2108 const boost::system::error_code ec,
2109 const std::vector<std::string> &resp) {
2110 if (ec)
2111 {
2112 if (ec.value() !=
2113 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002114 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002115 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2116 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002117 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002118 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002119 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002120 }
2121 asyncResp->res.jsonValue["@odata.type"] =
2122 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002123 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002124 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002125 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002126 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002127 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002128 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
2129 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002130 std::vector<std::string> logIDs;
2131 // Get the list of log entries and build up an empty array big
2132 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07002133 for (const std::string &objpath : resp)
2134 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002135 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002136 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002137 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002138 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002139 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002140 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002141 logIDs.emplace_back(objpath.substr(lastPos + 1));
2142
2143 // Add a space for the log entry to the array
2144 logEntryArray.push_back({});
2145 }
2146 // Now go through and set up async calls to fill in the entries
2147 size_t index = 0;
2148 for (const std::string &logID : logIDs)
2149 {
2150 // Add the log entry to the array
2151 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002152 }
2153 asyncResp->res.jsonValue["Members@odata.count"] =
2154 logEntryArray.size();
2155 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002156 crow::connections::systemBus->async_method_call(
2157 std::move(getLogEntriesCallback),
2158 "xyz.openbmc_project.ObjectMapper",
2159 "/xyz/openbmc_project/object_mapper",
2160 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002161 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002162 }
2163};
2164
Jason M. Bills424c4172019-03-21 13:50:33 -07002165class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002166{
2167 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002168 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002169 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002170 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002171 std::string())
2172 {
AppaRao Puli39460282020-04-07 17:03:04 +05302173 // Note: Deviated from redfish privilege registry for GET & HEAD
2174 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002175 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302176 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2177 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002178 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2179 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2180 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2181 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002182 }
2183
2184 private:
2185 void doGet(crow::Response &res, const crow::Request &req,
2186 const std::vector<std::string> &params) override
2187 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002188 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002189 if (params.size() != 1)
2190 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002191 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002192 return;
2193 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002194 const std::string &logID = params[0];
2195 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2196 }
2197};
2198
2199class CrashdumpFile : public Node
2200{
2201 public:
2202 CrashdumpFile(CrowApp &app) :
2203 Node(app,
2204 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2205 "<str>/",
2206 std::string(), std::string())
2207 {
AppaRao Puli39460282020-04-07 17:03:04 +05302208 // Note: Deviated from redfish privilege registry for GET & HEAD
2209 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002210 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302211 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2212 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002213 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2214 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2215 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2216 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2217 }
2218
2219 private:
2220 void doGet(crow::Response &res, const crow::Request &req,
2221 const std::vector<std::string> &params) override
2222 {
2223 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2224 if (params.size() != 2)
2225 {
2226 messages::internalError(asyncResp->res);
2227 return;
2228 }
2229 const std::string &logID = params[0];
2230 const std::string &fileName = params[1];
2231
Johnathan Mantey043a0532020-03-10 17:15:28 -07002232 auto getStoredLogCallback =
2233 [asyncResp, logID, fileName](
2234 const boost::system::error_code ec,
2235 const std::vector<std::pair<std::string, VariantType>> &resp) {
2236 if (ec)
2237 {
2238 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2239 << ec.message();
2240 messages::internalError(asyncResp->res);
2241 return;
2242 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002243
Johnathan Mantey043a0532020-03-10 17:15:28 -07002244 std::string dbusFilename{};
2245 std::string dbusTimestamp{};
2246 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002247
Johnathan Mantey043a0532020-03-10 17:15:28 -07002248 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2249 dbusFilepath);
2250
2251 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2252 dbusFilepath.empty())
2253 {
2254 messages::resourceMissingAtURI(asyncResp->res, fileName);
2255 return;
2256 }
2257
2258 // Verify the file name parameter is correct
2259 if (fileName != dbusFilename)
2260 {
2261 messages::resourceMissingAtURI(asyncResp->res, fileName);
2262 return;
2263 }
2264
2265 if (!std::filesystem::exists(dbusFilepath))
2266 {
2267 messages::resourceMissingAtURI(asyncResp->res, fileName);
2268 return;
2269 }
2270 std::ifstream ifs(dbusFilepath, std::ios::in |
2271 std::ios::binary |
2272 std::ios::ate);
2273 std::ifstream::pos_type fileSize = ifs.tellg();
2274 if (fileSize < 0)
2275 {
2276 messages::generalError(asyncResp->res);
2277 return;
2278 }
2279 ifs.seekg(0, std::ios::beg);
2280
2281 auto crashData = std::make_unique<char[]>(
2282 static_cast<unsigned int>(fileSize));
2283
2284 ifs.read(crashData.get(), static_cast<int>(fileSize));
2285
2286 // The cast to std::string is intentional in order to use the
2287 // assign() that applies move mechanics
2288 asyncResp->res.body().assign(
2289 static_cast<std::string>(crashData.get()));
2290
2291 // Configure this to be a file download when accessed from
2292 // a browser
2293 asyncResp->res.addHeader("Content-Disposition", "attachment");
2294 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002295 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002296 std::move(getStoredLogCallback), crashdumpObject,
2297 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002298 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002299 }
2300};
2301
Jason M. Bills424c4172019-03-21 13:50:33 -07002302class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002303{
2304 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002305 OnDemandCrashdump(CrowApp &app) :
2306 Node(app,
2307 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2308 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002309 {
AppaRao Puli39460282020-04-07 17:03:04 +05302310 // Note: Deviated from redfish privilege registry for GET & HEAD
2311 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002312 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302313 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2314 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2315 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2316 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2317 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2318 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002319 }
2320
2321 private:
2322 void doPost(crow::Response &res, const crow::Request &req,
2323 const std::vector<std::string> &params) override
2324 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002325 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002326
James Feistfe306722020-03-12 16:32:08 -07002327 auto generateonDemandLogCallback = [asyncResp,
2328 req](const boost::system::error_code
2329 ec,
2330 const std::string &resp) {
James Feist46229572020-02-19 15:11:58 -08002331 if (ec)
2332 {
2333 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002334 {
James Feist46229572020-02-19 15:11:58 -08002335 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002336 }
James Feist46229572020-02-19 15:11:58 -08002337 else if (ec.value() ==
2338 boost::system::errc::device_or_resource_busy)
2339 {
2340 messages::serviceTemporarilyUnavailable(asyncResp->res,
2341 "60");
2342 }
2343 else
2344 {
2345 messages::internalError(asyncResp->res);
2346 }
2347 return;
2348 }
2349 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08002350 [](boost::system::error_code err, sdbusplus::message::message &,
2351 const std::shared_ptr<task::TaskData> &taskData) {
2352 if (!err)
2353 {
James Feiste5d50062020-05-11 17:29:00 -07002354 taskData->messages.emplace_back(
2355 messages::taskCompletedOK(
2356 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002357 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002358 }
James Feist32898ce2020-03-10 16:16:52 -07002359 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002360 },
James Feist46229572020-02-19 15:11:58 -08002361 "type='signal',interface='org.freedesktop.DBus.Properties',"
2362 "member='PropertiesChanged',arg0namespace='com.intel."
2363 "crashdump'");
2364 task->startTimer(std::chrono::minutes(5));
2365 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002366 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002367 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002368 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002369 std::move(generateonDemandLogCallback), crashdumpObject,
2370 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002371 }
2372};
2373
Jason M. Billse1f26342018-07-18 12:12:00 -07002374class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002375{
2376 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07002377 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002378 Node(app,
2379 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2380 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002381 {
AppaRao Puli39460282020-04-07 17:03:04 +05302382 // Note: Deviated from redfish privilege registry for GET & HEAD
2383 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002384 entityPrivileges = {
2385 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2386 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2387 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2388 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2389 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2390 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2391 }
2392
2393 private:
2394 void doPost(crow::Response &res, const crow::Request &req,
2395 const std::vector<std::string> &params) override
2396 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002397 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002398 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002399
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002400 nlohmann::json reqJson =
2401 nlohmann::json::parse(req.body, nullptr, false);
2402 if (reqJson.find("PECICommands") != reqJson.end())
2403 {
2404 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2405 {
2406 return;
2407 }
2408 uint32_t idx = 0;
2409 for (auto const &cmd : peciCommands)
2410 {
2411 if (cmd.size() < 3)
2412 {
2413 std::string s("[");
2414 for (auto const &val : cmd)
2415 {
2416 if (val != *cmd.begin())
2417 {
2418 s += ",";
2419 }
2420 s += std::to_string(val);
2421 }
2422 s += "]";
2423 messages::actionParameterValueFormatError(
2424 res, s, "PECICommands[" + std::to_string(idx) + "]",
2425 "SendRawPeci");
2426 return;
2427 }
2428 idx++;
2429 }
2430 }
2431 else
2432 {
2433 /* This interface is deprecated */
2434 uint8_t clientAddress = 0;
2435 uint8_t readLength = 0;
2436 std::vector<uint8_t> peciCommand;
2437 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2438 "ReadLength", readLength, "PECICommand",
2439 peciCommand))
2440 {
2441 return;
2442 }
2443 peciCommands.push_back({clientAddress, 0, readLength});
2444 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2445 peciCommand.end());
2446 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002447 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002448 auto sendRawPECICallback =
2449 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002450 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002451 if (ec)
2452 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002453 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002454 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002455 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002456 return;
2457 }
2458 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2459 {"PECIResponse", resp}};
2460 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002461 // Call the SendRawPECI command with the provided data
2462 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002463 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002464 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002465 }
2466};
2467
Andrew Geisslercb92c032018-08-17 07:56:14 -07002468/**
2469 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2470 */
2471class DBusLogServiceActionsClear : public Node
2472{
2473 public:
2474 DBusLogServiceActionsClear(CrowApp &app) :
2475 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002476 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002477 {
2478 entityPrivileges = {
2479 {boost::beast::http::verb::get, {{"Login"}}},
2480 {boost::beast::http::verb::head, {{"Login"}}},
2481 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2482 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2483 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2484 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2485 }
2486
2487 private:
2488 /**
2489 * Function handles POST method request.
2490 * The Clear Log actions does not require any parameter.The action deletes
2491 * all entries found in the Entries collection for this Log Service.
2492 */
2493 void doPost(crow::Response &res, const crow::Request &req,
2494 const std::vector<std::string> &params) override
2495 {
2496 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2497
2498 auto asyncResp = std::make_shared<AsyncResp>(res);
2499 // Process response from Logging service.
2500 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2501 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2502 if (ec)
2503 {
2504 // TODO Handle for specific error code
2505 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2506 asyncResp->res.result(
2507 boost::beast::http::status::internal_server_error);
2508 return;
2509 }
2510
2511 asyncResp->res.result(boost::beast::http::status::no_content);
2512 };
2513
2514 // Make call to Logging service to request Clear Log
2515 crow::connections::systemBus->async_method_call(
2516 resp_handler, "xyz.openbmc_project.Logging",
2517 "/xyz/openbmc_project/logging",
2518 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2519 }
2520};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002521
2522/****************************************************
2523 * Redfish PostCode interfaces
2524 * using DBUS interface: getPostCodesTS
2525 ******************************************************/
2526class PostCodesLogService : public Node
2527{
2528 public:
2529 PostCodesLogService(CrowApp &app) :
2530 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2531 {
2532 entityPrivileges = {
2533 {boost::beast::http::verb::get, {{"Login"}}},
2534 {boost::beast::http::verb::head, {{"Login"}}},
2535 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2536 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2537 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2538 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2539 }
2540
2541 private:
2542 void doGet(crow::Response &res, const crow::Request &req,
2543 const std::vector<std::string> &params) override
2544 {
2545 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2546
2547 asyncResp->res.jsonValue = {
2548 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2549 {"@odata.type", "#LogService.v1_1_0.LogService"},
2550 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2551 {"Name", "POST Code Log Service"},
2552 {"Description", "POST Code Log Service"},
2553 {"Id", "BIOS POST Code Log"},
2554 {"OverWritePolicy", "WrapsWhenFull"},
2555 {"Entries",
2556 {{"@odata.id",
2557 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2558 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2559 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2560 "Actions/LogService.ClearLog"}};
2561 }
2562};
2563
2564class PostCodesClear : public Node
2565{
2566 public:
2567 PostCodesClear(CrowApp &app) :
2568 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2569 "LogService.ClearLog/")
2570 {
2571 entityPrivileges = {
2572 {boost::beast::http::verb::get, {{"Login"}}},
2573 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302574 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2575 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2576 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2577 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002578 }
2579
2580 private:
2581 void doPost(crow::Response &res, const crow::Request &req,
2582 const std::vector<std::string> &params) override
2583 {
2584 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2585
2586 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2587 // Make call to post-code service to request clear all
2588 crow::connections::systemBus->async_method_call(
2589 [asyncResp](const boost::system::error_code ec) {
2590 if (ec)
2591 {
2592 // TODO Handle for specific error code
2593 BMCWEB_LOG_ERROR
2594 << "doClearPostCodes resp_handler got error " << ec;
2595 asyncResp->res.result(
2596 boost::beast::http::status::internal_server_error);
2597 messages::internalError(asyncResp->res);
2598 return;
2599 }
2600 },
2601 "xyz.openbmc_project.State.Boot.PostCode",
2602 "/xyz/openbmc_project/State/Boot/PostCode",
2603 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2604 }
2605};
2606
2607static void fillPostCodeEntry(
2608 std::shared_ptr<AsyncResp> aResp,
2609 const boost::container::flat_map<uint64_t, uint64_t> &postcode,
2610 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2611 const uint64_t skip = 0, const uint64_t top = 0)
2612{
2613 // Get the Message from the MessageRegistry
2614 const message_registries::Message *message =
2615 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
2616 std::string severity;
2617 if (message != nullptr)
2618 {
2619 severity = message->severity;
2620 }
2621
2622 uint64_t currentCodeIndex = 0;
2623 nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"];
2624
2625 uint64_t firstCodeTimeUs = 0;
2626 for (const std::pair<uint64_t, uint64_t> &code : postcode)
2627 {
2628 currentCodeIndex++;
2629 std::string postcodeEntryID =
2630 "B" + std::to_string(bootIndex) + "-" +
2631 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2632
2633 uint64_t usecSinceEpoch = code.first;
2634 uint64_t usTimeOffset = 0;
2635
2636 if (1 == currentCodeIndex)
2637 { // already incremented
2638 firstCodeTimeUs = code.first;
2639 }
2640 else
2641 {
2642 usTimeOffset = code.first - firstCodeTimeUs;
2643 }
2644
2645 // skip if no specific codeIndex is specified and currentCodeIndex does
2646 // not fall between top and skip
2647 if ((codeIndex == 0) &&
2648 (currentCodeIndex <= skip || currentCodeIndex > top))
2649 {
2650 continue;
2651 }
2652
2653 // skip if a sepcific codeIndex is specified and does not match the
2654 // currentIndex
2655 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2656 {
2657 // This is done for simplicity. 1st entry is needed to calculate
2658 // time offset. To improve efficiency, one can get to the entry
2659 // directly (possibly with flatmap's nth method)
2660 continue;
2661 }
2662
2663 // currentCodeIndex is within top and skip or equal to specified code
2664 // index
2665
2666 // Get the Created time from the timestamp
2667 std::string entryTimeStr;
2668 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2669 {
2670 continue;
2671 }
2672
2673 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2674 std::ostringstream hexCode;
2675 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2676 << code.second;
2677 std::ostringstream timeOffsetStr;
2678 // Set Fixed -Point Notation
2679 timeOffsetStr << std::fixed;
2680 // Set precision to 4 digits
2681 timeOffsetStr << std::setprecision(4);
2682 // Add double to stream
2683 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2684 std::vector<std::string> messageArgs = {
2685 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2686
2687 // Get MessageArgs template from message registry
2688 std::string msg;
2689 if (message != nullptr)
2690 {
2691 msg = message->message;
2692
2693 // fill in this post code value
2694 int i = 0;
2695 for (const std::string &messageArg : messageArgs)
2696 {
2697 std::string argStr = "%" + std::to_string(++i);
2698 size_t argPos = msg.find(argStr);
2699 if (argPos != std::string::npos)
2700 {
2701 msg.replace(argPos, argStr.length(), messageArg);
2702 }
2703 }
2704 }
2705
2706 // add to AsyncResp
2707 logEntryArray.push_back({});
2708 nlohmann::json &bmcLogEntry = logEntryArray.back();
2709 bmcLogEntry = {
2710 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2711 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2712 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2713 "PostCodes/Entries/" +
2714 postcodeEntryID},
2715 {"Name", "POST Code Log Entry"},
2716 {"Id", postcodeEntryID},
2717 {"Message", std::move(msg)},
2718 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2719 {"MessageArgs", std::move(messageArgs)},
2720 {"EntryType", "Event"},
2721 {"Severity", std::move(severity)},
2722 {"Created", std::move(entryTimeStr)}};
2723 }
2724}
2725
2726static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2727 const uint16_t bootIndex,
2728 const uint64_t codeIndex)
2729{
2730 crow::connections::systemBus->async_method_call(
2731 [aResp, bootIndex, codeIndex](
2732 const boost::system::error_code ec,
2733 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2734 if (ec)
2735 {
2736 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2737 messages::internalError(aResp->res);
2738 return;
2739 }
2740
2741 // skip the empty postcode boots
2742 if (postcode.empty())
2743 {
2744 return;
2745 }
2746
2747 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2748
2749 aResp->res.jsonValue["Members@odata.count"] =
2750 aResp->res.jsonValue["Members"].size();
2751 },
2752 "xyz.openbmc_project.State.Boot.PostCode",
2753 "/xyz/openbmc_project/State/Boot/PostCode",
2754 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2755 bootIndex);
2756}
2757
2758static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2759 const uint16_t bootIndex,
2760 const uint16_t bootCount,
2761 const uint64_t entryCount, const uint64_t skip,
2762 const uint64_t top)
2763{
2764 crow::connections::systemBus->async_method_call(
2765 [aResp, bootIndex, bootCount, entryCount, skip,
2766 top](const boost::system::error_code ec,
2767 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2768 if (ec)
2769 {
2770 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2771 messages::internalError(aResp->res);
2772 return;
2773 }
2774
2775 uint64_t endCount = entryCount;
2776 if (!postcode.empty())
2777 {
2778 endCount = entryCount + postcode.size();
2779
2780 if ((skip < endCount) && ((top + skip) > entryCount))
2781 {
2782 uint64_t thisBootSkip =
2783 std::max(skip, entryCount) - entryCount;
2784 uint64_t thisBootTop =
2785 std::min(top + skip, endCount) - entryCount;
2786
2787 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2788 thisBootSkip, thisBootTop);
2789 }
2790 aResp->res.jsonValue["Members@odata.count"] = endCount;
2791 }
2792
2793 // continue to previous bootIndex
2794 if (bootIndex < bootCount)
2795 {
2796 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2797 bootCount, endCount, skip, top);
2798 }
2799 else
2800 {
2801 aResp->res.jsonValue["Members@odata.nextLink"] =
2802 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2803 "Entries?$skip=" +
2804 std::to_string(skip + top);
2805 }
2806 },
2807 "xyz.openbmc_project.State.Boot.PostCode",
2808 "/xyz/openbmc_project/State/Boot/PostCode",
2809 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2810 bootIndex);
2811}
2812
2813static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2814 const uint64_t skip, const uint64_t top)
2815{
2816 uint64_t entryCount = 0;
2817 crow::connections::systemBus->async_method_call(
2818 [aResp, entryCount, skip,
2819 top](const boost::system::error_code ec,
2820 const std::variant<uint16_t> &bootCount) {
2821 if (ec)
2822 {
2823 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2824 messages::internalError(aResp->res);
2825 return;
2826 }
2827 auto pVal = std::get_if<uint16_t>(&bootCount);
2828 if (pVal)
2829 {
2830 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2831 }
2832 else
2833 {
2834 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2835 }
2836 },
2837 "xyz.openbmc_project.State.Boot.PostCode",
2838 "/xyz/openbmc_project/State/Boot/PostCode",
2839 "org.freedesktop.DBus.Properties", "Get",
2840 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2841}
2842
2843class PostCodesEntryCollection : public Node
2844{
2845 public:
2846 template <typename CrowApp>
2847 PostCodesEntryCollection(CrowApp &app) :
2848 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2849 {
2850 entityPrivileges = {
2851 {boost::beast::http::verb::get, {{"Login"}}},
2852 {boost::beast::http::verb::head, {{"Login"}}},
2853 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2854 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2855 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2856 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2857 }
2858
2859 private:
2860 void doGet(crow::Response &res, const crow::Request &req,
2861 const std::vector<std::string> &params) override
2862 {
2863 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2864
2865 asyncResp->res.jsonValue["@odata.type"] =
2866 "#LogEntryCollection.LogEntryCollection";
2867 asyncResp->res.jsonValue["@odata.context"] =
2868 "/redfish/v1/"
2869 "$metadata#LogEntryCollection.LogEntryCollection";
2870 asyncResp->res.jsonValue["@odata.id"] =
2871 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2872 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2873 asyncResp->res.jsonValue["Description"] =
2874 "Collection of POST Code Log Entries";
2875 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2876 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2877
2878 uint64_t skip = 0;
2879 uint64_t top = maxEntriesPerPage; // Show max entries by default
2880 if (!getSkipParam(asyncResp->res, req, skip))
2881 {
2882 return;
2883 }
2884 if (!getTopParam(asyncResp->res, req, top))
2885 {
2886 return;
2887 }
2888 getCurrentBootNumber(asyncResp, skip, top);
2889 }
2890};
2891
2892class PostCodesEntry : public Node
2893{
2894 public:
2895 PostCodesEntry(CrowApp &app) :
2896 Node(app,
2897 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2898 std::string())
2899 {
2900 entityPrivileges = {
2901 {boost::beast::http::verb::get, {{"Login"}}},
2902 {boost::beast::http::verb::head, {{"Login"}}},
2903 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2904 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2905 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2906 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2907 }
2908
2909 private:
2910 void doGet(crow::Response &res, const crow::Request &req,
2911 const std::vector<std::string> &params) override
2912 {
2913 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2914 if (params.size() != 1)
2915 {
2916 messages::internalError(asyncResp->res);
2917 return;
2918 }
2919
2920 const std::string &targetID = params[0];
2921
2922 size_t bootPos = targetID.find('B');
2923 if (bootPos == std::string::npos)
2924 {
2925 // Requested ID was not found
2926 messages::resourceMissingAtURI(asyncResp->res, targetID);
2927 return;
2928 }
2929 std::string_view bootIndexStr(targetID);
2930 bootIndexStr.remove_prefix(bootPos + 1);
2931 uint16_t bootIndex = 0;
2932 uint64_t codeIndex = 0;
2933 size_t dashPos = bootIndexStr.find('-');
2934
2935 if (dashPos == std::string::npos)
2936 {
2937 return;
2938 }
2939 std::string_view codeIndexStr(bootIndexStr);
2940 bootIndexStr.remove_suffix(dashPos);
2941 codeIndexStr.remove_prefix(dashPos + 1);
2942
2943 bootIndex = static_cast<uint16_t>(
2944 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2945 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2946 if (bootIndex == 0 || codeIndex == 0)
2947 {
2948 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2949 << params[0];
2950 }
2951
2952 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2953 asyncResp->res.jsonValue["@odata.context"] =
2954 "/redfish/v1/$metadata#LogEntry.LogEntry";
2955 asyncResp->res.jsonValue["@odata.id"] =
2956 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2957 "Entries";
2958 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2959 asyncResp->res.jsonValue["Description"] =
2960 "Collection of POST Code Log Entries";
2961 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2962 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2963
2964 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2965 }
2966};
2967
Ed Tanous1da66f72018-07-27 16:13:37 -07002968} // namespace redfish