blob: 767a7fcf14fa28677234fa22c6ebe7f93a7682f5 [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>
Andrew Geisslercb92c032018-08-17 07:56:14 -070030#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070031#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070032#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080033#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070034
35namespace redfish
36{
37
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070038constexpr char const *crashdumpObject = "com.intel.crashdump";
39constexpr char const *crashdumpPath = "/com/intel/crashdump";
40constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
41constexpr char const *crashdumpInterface = "com.intel.crashdump";
42constexpr char const *deleteAllInterface =
43 "xyz.openbmc_project.Collection.DeleteAll";
44constexpr char const *crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070045 "com.intel.crashdump.OnDemand";
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070046constexpr char const *crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070047 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070048
Jason M. Bills4851d452019-03-28 11:27:48 -070049namespace message_registries
50{
51static const Message *getMessageFromRegistry(
52 const std::string &messageKey,
53 const boost::beast::span<const MessageEntry> registry)
54{
55 boost::beast::span<const MessageEntry>::const_iterator messageIt =
56 std::find_if(registry.cbegin(), registry.cend(),
57 [&messageKey](const MessageEntry &messageEntry) {
58 return !std::strcmp(messageEntry.first,
59 messageKey.c_str());
60 });
61 if (messageIt != registry.cend())
62 {
63 return &messageIt->second;
64 }
65
66 return nullptr;
67}
68
69static const Message *getMessage(const std::string_view &messageID)
70{
71 // Redfish MessageIds are in the form
72 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
73 // the right Message
74 std::vector<std::string> fields;
75 fields.reserve(4);
76 boost::split(fields, messageID, boost::is_any_of("."));
77 std::string &registryName = fields[0];
78 std::string &messageKey = fields[3];
79
80 // Find the right registry and check it for the MessageKey
81 if (std::string(base::header.registryPrefix) == registryName)
82 {
83 return getMessageFromRegistry(
84 messageKey, boost::beast::span<const MessageEntry>(base::registry));
85 }
86 if (std::string(openbmc::header.registryPrefix) == registryName)
87 {
88 return getMessageFromRegistry(
89 messageKey,
90 boost::beast::span<const MessageEntry>(openbmc::registry));
91 }
92 return nullptr;
93}
94} // namespace message_registries
95
James Feistf6150402019-01-08 10:36:20 -080096namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070097
Andrew Geisslercb92c032018-08-17 07:56:14 -070098using GetManagedPropertyType = boost::container::flat_map<
99 std::string,
100 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
101 int32_t, uint32_t, int64_t, uint64_t, double>>;
102
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 }
159
160 asyncResp->res.result(boost::beast::http::status::ok);
161 };
162 crow::connections::systemBus->async_method_call(
163 respHandler, "xyz.openbmc_project.Dump.Manager",
164 "/xyz/openbmc_project/dump/entry/" + entryID,
165 "xyz.openbmc_project.Object.Delete", "Delete");
166}
167
Jason M. Bills16428a12018-11-02 12:42:29 -0700168static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800169 const std::string_view &field,
170 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700171{
172 const char *data = nullptr;
173 size_t length = 0;
174 int ret = 0;
175 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700176 ret = sd_journal_get_data(journal, field.data(),
177 reinterpret_cast<const void **>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700178 if (ret < 0)
179 {
180 return ret;
181 }
Ed Tanous39e77502019-03-04 17:35:53 -0800182 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700183 // Only use the content after the "=" character.
184 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
185 return ret;
186}
187
188static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800189 const std::string_view &field, const int &base,
Ed Tanous271584a2019-07-09 16:24:22 -0700190 long int &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700191{
192 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800193 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700194 // Get the metadata from the requested field of the journal entry
195 ret = getJournalMetadata(journal, field, metadata);
196 if (ret < 0)
197 {
198 return ret;
199 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000200 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700201 return ret;
202}
203
ZhikuiRena3316fc2020-01-29 14:58:08 -0800204static bool getTimestampStr(const uint64_t usecSinceEpoch,
205 std::string &entryTimestamp)
Jason M. Bills16428a12018-11-02 12:42:29 -0700206{
ZhikuiRena3316fc2020-01-29 14:58:08 -0800207 time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000);
Jason M. Bills16428a12018-11-02 12:42:29 -0700208 struct tm *loctime = localtime(&t);
209 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700210 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700211 {
212 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
213 }
214 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800215 std::string_view t1(entryTime);
216 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700217 if (t1.size() > 2 && t2.size() > 2)
218 {
219 t1.remove_suffix(2);
220 t2.remove_prefix(t2.size() - 2);
221 }
Ed Tanous39e77502019-03-04 17:35:53 -0800222 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700223 return true;
224}
225
ZhikuiRena3316fc2020-01-29 14:58:08 -0800226static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
227{
228 int ret = 0;
229 uint64_t timestamp = 0;
230 ret = sd_journal_get_realtime_usec(journal, &timestamp);
231 if (ret < 0)
232 {
233 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
234 << strerror(-ret);
235 return false;
236 }
237 return getTimestampStr(timestamp, entryTimestamp);
238}
239
Jason M. Bills16428a12018-11-02 12:42:29 -0700240static bool getSkipParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700241 uint64_t &skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700242{
243 char *skipParam = req.urlParams.get("$skip");
244 if (skipParam != nullptr)
245 {
246 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700247 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700248 if (*skipParam == '\0' || *ptr != '\0')
249 {
250
251 messages::queryParameterValueTypeError(res, std::string(skipParam),
252 "$skip");
253 return false;
254 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700255 }
256 return true;
257}
258
Ed Tanous271584a2019-07-09 16:24:22 -0700259static constexpr const uint64_t maxEntriesPerPage = 1000;
Jason M. Bills16428a12018-11-02 12:42:29 -0700260static bool getTopParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700261 uint64_t &top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700262{
263 char *topParam = req.urlParams.get("$top");
264 if (topParam != nullptr)
265 {
266 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700267 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700268 if (*topParam == '\0' || *ptr != '\0')
269 {
270 messages::queryParameterValueTypeError(res, std::string(topParam),
271 "$top");
272 return false;
273 }
Ed Tanous271584a2019-07-09 16:24:22 -0700274 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700275 {
276
277 messages::queryParameterOutOfRange(
278 res, std::to_string(top), "$top",
279 "1-" + std::to_string(maxEntriesPerPage));
280 return false;
281 }
282 }
283 return true;
284}
285
Jason M. Billse85d6b12019-07-29 17:01:15 -0700286static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
287 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700288{
289 int ret = 0;
290 static uint64_t prevTs = 0;
291 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700292 if (firstEntry)
293 {
294 prevTs = 0;
295 }
296
Jason M. Bills16428a12018-11-02 12:42:29 -0700297 // Get the entry timestamp
298 uint64_t curTs = 0;
299 ret = sd_journal_get_realtime_usec(journal, &curTs);
300 if (ret < 0)
301 {
302 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
303 << strerror(-ret);
304 return false;
305 }
306 // If the timestamp isn't unique, increment the index
307 if (curTs == prevTs)
308 {
309 index++;
310 }
311 else
312 {
313 // Otherwise, reset it
314 index = 0;
315 }
316 // Save the timestamp
317 prevTs = curTs;
318
319 entryID = std::to_string(curTs);
320 if (index > 0)
321 {
322 entryID += "_" + std::to_string(index);
323 }
324 return true;
325}
326
Jason M. Billse85d6b12019-07-29 17:01:15 -0700327static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
328 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700329{
Ed Tanous271584a2019-07-09 16:24:22 -0700330 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700331 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700332 if (firstEntry)
333 {
334 prevTs = 0;
335 }
336
Jason M. Bills95820182019-04-22 16:25:34 -0700337 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700338 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700339 std::tm timeStruct = {};
340 std::istringstream entryStream(logEntry);
341 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
342 {
343 curTs = std::mktime(&timeStruct);
344 }
345 // If the timestamp isn't unique, increment the index
346 if (curTs == prevTs)
347 {
348 index++;
349 }
350 else
351 {
352 // Otherwise, reset it
353 index = 0;
354 }
355 // Save the timestamp
356 prevTs = curTs;
357
358 entryID = std::to_string(curTs);
359 if (index > 0)
360 {
361 entryID += "_" + std::to_string(index);
362 }
363 return true;
364}
365
Jason M. Bills16428a12018-11-02 12:42:29 -0700366static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
Ed Tanous271584a2019-07-09 16:24:22 -0700367 uint64_t &timestamp, uint64_t &index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700368{
369 if (entryID.empty())
370 {
371 return false;
372 }
373 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800374 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700375
376 auto underscorePos = tsStr.find("_");
377 if (underscorePos != tsStr.npos)
378 {
379 // Timestamp has an index
380 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800381 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700382 indexStr.remove_prefix(underscorePos + 1);
383 std::size_t pos;
384 try
385 {
Ed Tanous39e77502019-03-04 17:35:53 -0800386 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700387 }
Ed Tanous271584a2019-07-09 16:24:22 -0700388 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700389 {
390 messages::resourceMissingAtURI(res, entryID);
391 return false;
392 }
Ed Tanous271584a2019-07-09 16:24:22 -0700393 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700394 {
395 messages::resourceMissingAtURI(res, entryID);
396 return false;
397 }
398 if (pos != indexStr.size())
399 {
400 messages::resourceMissingAtURI(res, entryID);
401 return false;
402 }
403 }
404 // Timestamp has no index
405 std::size_t pos;
406 try
407 {
Ed Tanous39e77502019-03-04 17:35:53 -0800408 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700409 }
Ed Tanous271584a2019-07-09 16:24:22 -0700410 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700411 {
412 messages::resourceMissingAtURI(res, entryID);
413 return false;
414 }
Ed Tanous271584a2019-07-09 16:24:22 -0700415 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700416 {
417 messages::resourceMissingAtURI(res, entryID);
418 return false;
419 }
420 if (pos != tsStr.size())
421 {
422 messages::resourceMissingAtURI(res, entryID);
423 return false;
424 }
425 return true;
426}
427
Jason M. Bills95820182019-04-22 16:25:34 -0700428static bool
429 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
430{
431 static const std::filesystem::path redfishLogDir = "/var/log";
432 static const std::string redfishLogFilename = "redfish";
433
434 // Loop through the directory looking for redfish log files
435 for (const std::filesystem::directory_entry &dirEnt :
436 std::filesystem::directory_iterator(redfishLogDir))
437 {
438 // If we find a redfish log file, save the path
439 std::string filename = dirEnt.path().filename();
440 if (boost::starts_with(filename, redfishLogFilename))
441 {
442 redfishLogFiles.emplace_back(redfishLogDir / filename);
443 }
444 }
445 // As the log files rotate, they are appended with a ".#" that is higher for
446 // the older logs. Since we don't expect more than 10 log files, we
447 // can just sort the list to get them in order from newest to oldest
448 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
449
450 return !redfishLogFiles.empty();
451}
452
Johnathan Mantey043a0532020-03-10 17:15:28 -0700453static void ParseCrashdumpParameters(
454 const std::vector<std::pair<std::string, VariantType>> &params,
455 std::string &filename, std::string &timestamp, std::string &logfile)
456{
457 for (auto property : params)
458 {
459 if (property.first == "Timestamp")
460 {
461 const std::string *value =
462 sdbusplus::message::variant_ns::get_if<std::string>(
463 &property.second);
464 if (value != nullptr)
465 {
466 timestamp = *value;
467 }
468 }
469 else if (property.first == "Filename")
470 {
471 const std::string *value =
472 sdbusplus::message::variant_ns::get_if<std::string>(
473 &property.second);
474 if (value != nullptr)
475 {
476 filename = *value;
477 }
478 }
479 else if (property.first == "Log")
480 {
481 const std::string *value =
482 sdbusplus::message::variant_ns::get_if<std::string>(
483 &property.second);
484 if (value != nullptr)
485 {
486 logfile = *value;
487 }
488 }
489 }
490}
491
ZhikuiRena3316fc2020-01-29 14:58:08 -0800492constexpr char const *postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800493class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700494{
495 public:
496 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800497 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800498 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800499 {
500 entityPrivileges = {
501 {boost::beast::http::verb::get, {{"Login"}}},
502 {boost::beast::http::verb::head, {{"Login"}}},
503 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
504 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
505 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
506 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
507 }
508
509 private:
510 /**
511 * Functions triggers appropriate requests on DBus
512 */
513 void doGet(crow::Response &res, const crow::Request &req,
514 const std::vector<std::string> &params) override
515 {
516 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800517 // Collections don't include the static data added by SubRoute because
518 // it has a duplicate entry for members
519 asyncResp->res.jsonValue["@odata.type"] =
520 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800521 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800522 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800523 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
524 asyncResp->res.jsonValue["Description"] =
525 "Collection of LogServices for this Computer System";
526 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
527 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800528 logServiceArray.push_back(
529 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600530#ifdef BMCWEB_ENABLE_REDFISH_SYSTEMDUMP_LOG
531 logServiceArray.push_back(
532 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/System"}});
533#endif
534
Jason M. Billsd53dd412019-02-12 17:16:22 -0800535#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
536 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500537 {{"@odata.id",
538 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800539#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800540 asyncResp->res.jsonValue["Members@odata.count"] =
541 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800542
543 crow::connections::systemBus->async_method_call(
544 [asyncResp](const boost::system::error_code ec,
545 const std::vector<std::string> &subtreePath) {
546 if (ec)
547 {
548 BMCWEB_LOG_ERROR << ec;
549 return;
550 }
551
552 for (auto &pathStr : subtreePath)
553 {
554 if (pathStr.find("PostCode") != std::string::npos)
555 {
556 nlohmann::json &logServiceArray =
557 asyncResp->res.jsonValue["Members"];
558 logServiceArray.push_back(
559 {{"@odata.id", "/redfish/v1/Systems/system/"
560 "LogServices/PostCodes"}});
561 asyncResp->res.jsonValue["Members@odata.count"] =
562 logServiceArray.size();
563 return;
564 }
565 }
566 },
567 "xyz.openbmc_project.ObjectMapper",
568 "/xyz/openbmc_project/object_mapper",
569 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
570 std::array<const char *, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800571 }
572};
573
574class EventLogService : public Node
575{
576 public:
577 template <typename CrowApp>
578 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800579 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800580 {
581 entityPrivileges = {
582 {boost::beast::http::verb::get, {{"Login"}}},
583 {boost::beast::http::verb::head, {{"Login"}}},
584 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
585 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
586 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
587 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
588 }
589
590 private:
591 void doGet(crow::Response &res, const crow::Request &req,
592 const std::vector<std::string> &params) override
593 {
594 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
595
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800596 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800597 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800598 asyncResp->res.jsonValue["@odata.type"] =
599 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800600 asyncResp->res.jsonValue["Name"] = "Event Log Service";
601 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -0500602 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800603 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
604 asyncResp->res.jsonValue["Entries"] = {
605 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800606 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500607 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
608
609 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
610 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700611 }
612};
613
Tim Lee1f56a3a2019-10-09 10:17:57 +0800614class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700615{
616 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800617 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700618 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
619 "LogService.ClearLog/")
620 {
621 entityPrivileges = {
622 {boost::beast::http::verb::get, {{"Login"}}},
623 {boost::beast::http::verb::head, {{"Login"}}},
624 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
625 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
626 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
627 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
628 }
629
630 private:
631 void doPost(crow::Response &res, const crow::Request &req,
632 const std::vector<std::string> &params) override
633 {
634 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
635
636 // Clear the EventLog by deleting the log files
637 std::vector<std::filesystem::path> redfishLogFiles;
638 if (getRedfishLogFiles(redfishLogFiles))
639 {
640 for (const std::filesystem::path &file : redfishLogFiles)
641 {
642 std::error_code ec;
643 std::filesystem::remove(file, ec);
644 }
645 }
646
647 // Reload rsyslog so it knows to start new log files
648 crow::connections::systemBus->async_method_call(
649 [asyncResp](const boost::system::error_code ec) {
650 if (ec)
651 {
652 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
653 messages::internalError(asyncResp->res);
654 return;
655 }
656
657 messages::success(asyncResp->res);
658 },
659 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
660 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
661 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800662 }
663};
664
Jason M. Bills95820182019-04-22 16:25:34 -0700665static int fillEventLogEntryJson(const std::string &logEntryID,
666 const std::string logEntry,
667 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800668{
Jason M. Bills95820182019-04-22 16:25:34 -0700669 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700670 // First get the Timestamp
671 size_t space = logEntry.find_first_of(" ");
672 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700673 {
674 return 1;
675 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700676 std::string timestamp = logEntry.substr(0, space);
677 // Then get the log contents
678 size_t entryStart = logEntry.find_first_not_of(" ", space);
679 if (entryStart == std::string::npos)
680 {
681 return 1;
682 }
683 std::string_view entry(logEntry);
684 entry.remove_prefix(entryStart);
685 // Use split to separate the entry into its fields
686 std::vector<std::string> logEntryFields;
687 boost::split(logEntryFields, entry, boost::is_any_of(","),
688 boost::token_compress_on);
689 // We need at least a MessageId to be valid
690 if (logEntryFields.size() < 1)
691 {
692 return 1;
693 }
694 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700695
Jason M. Bills4851d452019-03-28 11:27:48 -0700696 // Get the Message from the MessageRegistry
697 const message_registries::Message *message =
698 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800699
Jason M. Bills4851d452019-03-28 11:27:48 -0700700 std::string msg;
701 std::string severity;
702 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800703 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700704 msg = message->message;
705 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800706 }
707
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700708 // Get the MessageArgs from the log if there are any
709 boost::beast::span<std::string> messageArgs;
710 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700711 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700712 std::string &messageArgsStart = logEntryFields[1];
713 // If the first string is empty, assume there are no MessageArgs
714 std::size_t messageArgsSize = 0;
715 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700716 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700717 messageArgsSize = logEntryFields.size() - 1;
718 }
719
720 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
721
722 // Fill the MessageArgs into the Message
723 int i = 0;
724 for (const std::string &messageArg : messageArgs)
725 {
726 std::string argStr = "%" + std::to_string(++i);
727 size_t argPos = msg.find(argStr);
728 if (argPos != std::string::npos)
729 {
730 msg.replace(argPos, argStr.length(), messageArg);
731 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700732 }
733 }
734
Jason M. Bills95820182019-04-22 16:25:34 -0700735 // Get the Created time from the timestamp. The log timestamp is in RFC3339
736 // format which matches the Redfish format except for the fractional seconds
737 // between the '.' and the '+', so just remove them.
738 std::size_t dot = timestamp.find_first_of(".");
739 std::size_t plus = timestamp.find_first_of("+");
740 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800741 {
Jason M. Bills95820182019-04-22 16:25:34 -0700742 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800743 }
744
745 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700746 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700747 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800748 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700749 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700750 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800751 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700752 {"Id", logEntryID},
753 {"Message", std::move(msg)},
754 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800755 {"MessageArgs", std::move(messageArgs)},
756 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700757 {"Severity", std::move(severity)},
758 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800759 return 0;
760}
761
Anthony Wilson27062602019-04-22 02:10:09 -0500762class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800763{
764 public:
765 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500766 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800767 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800768 {
769 entityPrivileges = {
770 {boost::beast::http::verb::get, {{"Login"}}},
771 {boost::beast::http::verb::head, {{"Login"}}},
772 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
773 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
774 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
775 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
776 }
777
778 private:
779 void doGet(crow::Response &res, const crow::Request &req,
780 const std::vector<std::string> &params) override
781 {
782 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700783 uint64_t skip = 0;
784 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800785 if (!getSkipParam(asyncResp->res, req, skip))
786 {
787 return;
788 }
789 if (!getTopParam(asyncResp->res, req, top))
790 {
791 return;
792 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800793 // Collections don't include the static data added by SubRoute because
794 // it has a duplicate entry for members
795 asyncResp->res.jsonValue["@odata.type"] =
796 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800797 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800798 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800799 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
800 asyncResp->res.jsonValue["Description"] =
801 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700802
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800803 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
804 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700805 // Go through the log files and create a unique ID for each entry
806 std::vector<std::filesystem::path> redfishLogFiles;
807 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000808 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700809 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700810
811 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700812 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
813 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800814 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700815 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700816 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800817 {
818 continue;
819 }
820
Jason M. Billse85d6b12019-07-29 17:01:15 -0700821 // Reset the unique ID on the first entry
822 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700823 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800824 {
Jason M. Bills95820182019-04-22 16:25:34 -0700825 entryCount++;
826 // Handle paging using skip (number of entries to skip from the
827 // start) and top (number of entries to display)
828 if (entryCount <= skip || entryCount > skip + top)
829 {
830 continue;
831 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800832
Jason M. Bills95820182019-04-22 16:25:34 -0700833 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700834 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700835 {
836 continue;
837 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800838
Jason M. Billse85d6b12019-07-29 17:01:15 -0700839 if (firstEntry)
840 {
841 firstEntry = false;
842 }
843
Jason M. Bills95820182019-04-22 16:25:34 -0700844 logEntryArray.push_back({});
845 nlohmann::json &bmcLogEntry = logEntryArray.back();
846 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
847 {
848 messages::internalError(asyncResp->res);
849 return;
850 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800851 }
852 }
853 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
854 if (skip + top < entryCount)
855 {
856 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700857 "/redfish/v1/Systems/system/LogServices/EventLog/"
858 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800859 std::to_string(skip + top);
860 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500861 }
862};
863
Jason M. Bills897967d2019-07-29 17:05:30 -0700864class JournalEventLogEntry : public Node
865{
866 public:
867 JournalEventLogEntry(CrowApp &app) :
868 Node(app,
869 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
870 std::string())
871 {
872 entityPrivileges = {
873 {boost::beast::http::verb::get, {{"Login"}}},
874 {boost::beast::http::verb::head, {{"Login"}}},
875 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
876 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
877 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
878 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
879 }
880
881 private:
882 void doGet(crow::Response &res, const crow::Request &req,
883 const std::vector<std::string> &params) override
884 {
885 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
886 if (params.size() != 1)
887 {
888 messages::internalError(asyncResp->res);
889 return;
890 }
891 const std::string &targetID = params[0];
892
893 // Go through the log files and check the unique ID for each entry to
894 // find the target entry
895 std::vector<std::filesystem::path> redfishLogFiles;
896 getRedfishLogFiles(redfishLogFiles);
897 std::string logEntry;
898
899 // Oldest logs are in the last file, so start there and loop backwards
900 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
901 it++)
902 {
903 std::ifstream logStream(*it);
904 if (!logStream.is_open())
905 {
906 continue;
907 }
908
909 // Reset the unique ID on the first entry
910 bool firstEntry = true;
911 while (std::getline(logStream, logEntry))
912 {
913 std::string idStr;
914 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
915 {
916 continue;
917 }
918
919 if (firstEntry)
920 {
921 firstEntry = false;
922 }
923
924 if (idStr == targetID)
925 {
926 if (fillEventLogEntryJson(idStr, logEntry,
927 asyncResp->res.jsonValue) != 0)
928 {
929 messages::internalError(asyncResp->res);
930 return;
931 }
932 return;
933 }
934 }
935 }
936 // Requested ID was not found
937 messages::resourceMissingAtURI(asyncResp->res, targetID);
938 }
939};
940
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500941class DBusEventLogEntryCollection : public Node
942{
943 public:
944 template <typename CrowApp>
945 DBusEventLogEntryCollection(CrowApp &app) :
946 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
947 {
948 entityPrivileges = {
949 {boost::beast::http::verb::get, {{"Login"}}},
950 {boost::beast::http::verb::head, {{"Login"}}},
951 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
952 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
953 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
954 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
955 }
956
957 private:
958 void doGet(crow::Response &res, const crow::Request &req,
959 const std::vector<std::string> &params) override
960 {
961 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
962
963 // Collections don't include the static data added by SubRoute because
964 // it has a duplicate entry for members
965 asyncResp->res.jsonValue["@odata.type"] =
966 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500967 asyncResp->res.jsonValue["@odata.id"] =
968 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
969 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
970 asyncResp->res.jsonValue["Description"] =
971 "Collection of System Event Log Entries";
972
Andrew Geisslercb92c032018-08-17 07:56:14 -0700973 // DBus implementation of EventLog/Entries
974 // Make call to Logging Service to find all log entry objects
975 crow::connections::systemBus->async_method_call(
976 [asyncResp](const boost::system::error_code ec,
977 GetManagedObjectsType &resp) {
978 if (ec)
979 {
980 // TODO Handle for specific error code
981 BMCWEB_LOG_ERROR
982 << "getLogEntriesIfaceData resp_handler got error "
983 << ec;
984 messages::internalError(asyncResp->res);
985 return;
986 }
987 nlohmann::json &entriesArray =
988 asyncResp->res.jsonValue["Members"];
989 entriesArray = nlohmann::json::array();
990 for (auto &objectPath : resp)
991 {
992 for (auto &interfaceMap : objectPath.second)
993 {
994 if (interfaceMap.first !=
995 "xyz.openbmc_project.Logging.Entry")
996 {
997 BMCWEB_LOG_DEBUG << "Bailing early on "
998 << interfaceMap.first;
999 continue;
1000 }
1001 entriesArray.push_back({});
1002 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -07001003 uint32_t *id = nullptr;
1004 std::time_t timestamp{};
1005 std::string *severity = nullptr;
1006 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001007 for (auto &propertyMap : interfaceMap.second)
1008 {
1009 if (propertyMap.first == "Id")
1010 {
1011 id = sdbusplus::message::variant_ns::get_if<
1012 uint32_t>(&propertyMap.second);
1013 if (id == nullptr)
1014 {
1015 messages::propertyMissing(asyncResp->res,
1016 "Id");
1017 }
1018 }
1019 else if (propertyMap.first == "Timestamp")
1020 {
1021 const uint64_t *millisTimeStamp =
1022 std::get_if<uint64_t>(&propertyMap.second);
1023 if (millisTimeStamp == nullptr)
1024 {
1025 messages::propertyMissing(asyncResp->res,
1026 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001027 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001028 }
1029 // Retrieve Created property with format:
1030 // yyyy-mm-ddThh:mm:ss
1031 std::chrono::milliseconds chronoTimeStamp(
1032 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001033 timestamp = std::chrono::duration_cast<
1034 std::chrono::duration<int>>(
1035 chronoTimeStamp)
1036 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001037 }
1038 else if (propertyMap.first == "Severity")
1039 {
1040 severity = std::get_if<std::string>(
1041 &propertyMap.second);
1042 if (severity == nullptr)
1043 {
1044 messages::propertyMissing(asyncResp->res,
1045 "Severity");
1046 }
1047 }
1048 else if (propertyMap.first == "Message")
1049 {
1050 message = std::get_if<std::string>(
1051 &propertyMap.second);
1052 if (message == nullptr)
1053 {
1054 messages::propertyMissing(asyncResp->res,
1055 "Message");
1056 }
1057 }
1058 }
1059 thisEntry = {
1060 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001061 {"@odata.id",
1062 "/redfish/v1/Systems/system/LogServices/EventLog/"
1063 "Entries/" +
1064 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001065 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001066 {"Id", std::to_string(*id)},
1067 {"Message", *message},
1068 {"EntryType", "Event"},
1069 {"Severity",
1070 translateSeverityDbusToRedfish(*severity)},
1071 {"Created", crow::utility::getDateTime(timestamp)}};
1072 }
1073 }
1074 std::sort(entriesArray.begin(), entriesArray.end(),
1075 [](const nlohmann::json &left,
1076 const nlohmann::json &right) {
1077 return (left["Id"] <= right["Id"]);
1078 });
1079 asyncResp->res.jsonValue["Members@odata.count"] =
1080 entriesArray.size();
1081 },
1082 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1083 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001084 }
1085};
1086
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001087class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001088{
1089 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001090 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001091 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001092 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1093 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001094 {
1095 entityPrivileges = {
1096 {boost::beast::http::verb::get, {{"Login"}}},
1097 {boost::beast::http::verb::head, {{"Login"}}},
1098 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1099 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1100 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1101 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1102 }
1103
1104 private:
1105 void doGet(crow::Response &res, const crow::Request &req,
1106 const std::vector<std::string> &params) override
1107 {
1108 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001109 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001110 {
1111 messages::internalError(asyncResp->res);
1112 return;
1113 }
Ed Tanous029573d2019-02-01 10:57:49 -08001114 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001115
Andrew Geisslercb92c032018-08-17 07:56:14 -07001116 // DBus implementation of EventLog/Entries
1117 // Make call to Logging Service to find all log entry objects
1118 crow::connections::systemBus->async_method_call(
1119 [asyncResp, entryID](const boost::system::error_code ec,
1120 GetManagedPropertyType &resp) {
1121 if (ec)
1122 {
1123 BMCWEB_LOG_ERROR
1124 << "EventLogEntry (DBus) resp_handler got error " << ec;
1125 messages::internalError(asyncResp->res);
1126 return;
1127 }
Ed Tanous66664f22019-10-11 13:05:49 -07001128 uint32_t *id = nullptr;
1129 std::time_t timestamp{};
1130 std::string *severity = nullptr;
1131 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001132 for (auto &propertyMap : resp)
1133 {
1134 if (propertyMap.first == "Id")
1135 {
1136 id = std::get_if<uint32_t>(&propertyMap.second);
1137 if (id == nullptr)
1138 {
1139 messages::propertyMissing(asyncResp->res, "Id");
1140 }
1141 }
1142 else if (propertyMap.first == "Timestamp")
1143 {
1144 const uint64_t *millisTimeStamp =
1145 std::get_if<uint64_t>(&propertyMap.second);
1146 if (millisTimeStamp == nullptr)
1147 {
1148 messages::propertyMissing(asyncResp->res,
1149 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001150 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001151 }
1152 // Retrieve Created property with format:
1153 // yyyy-mm-ddThh:mm:ss
1154 std::chrono::milliseconds chronoTimeStamp(
1155 *millisTimeStamp);
1156 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001157 std::chrono::duration_cast<
1158 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001159 .count();
1160 }
1161 else if (propertyMap.first == "Severity")
1162 {
1163 severity =
1164 std::get_if<std::string>(&propertyMap.second);
1165 if (severity == nullptr)
1166 {
1167 messages::propertyMissing(asyncResp->res,
1168 "Severity");
1169 }
1170 }
1171 else if (propertyMap.first == "Message")
1172 {
1173 message = std::get_if<std::string>(&propertyMap.second);
1174 if (message == nullptr)
1175 {
1176 messages::propertyMissing(asyncResp->res,
1177 "Message");
1178 }
1179 }
1180 }
Ed Tanous271584a2019-07-09 16:24:22 -07001181 if (id == nullptr || message == nullptr || severity == nullptr)
1182 {
1183 return;
1184 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001185 asyncResp->res.jsonValue = {
1186 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001187 {"@odata.id",
1188 "/redfish/v1/Systems/system/LogServices/EventLog/"
1189 "Entries/" +
1190 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001191 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001192 {"Id", std::to_string(*id)},
1193 {"Message", *message},
1194 {"EntryType", "Event"},
1195 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001196 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001197 },
1198 "xyz.openbmc_project.Logging",
1199 "/xyz/openbmc_project/logging/entry/" + entryID,
1200 "org.freedesktop.DBus.Properties", "GetAll",
1201 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001202 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001203
1204 void doDelete(crow::Response &res, const crow::Request &req,
1205 const std::vector<std::string> &params) override
1206 {
1207
1208 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1209
1210 auto asyncResp = std::make_shared<AsyncResp>(res);
1211
1212 if (params.size() != 1)
1213 {
1214 messages::internalError(asyncResp->res);
1215 return;
1216 }
1217 std::string entryID = params[0];
1218
1219 dbus::utility::escapePathForDbus(entryID);
1220
1221 // Process response from Logging service.
1222 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1223 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1224 if (ec)
1225 {
1226 // TODO Handle for specific error code
1227 BMCWEB_LOG_ERROR
1228 << "EventLogEntry (DBus) doDelete respHandler got error "
1229 << ec;
1230 asyncResp->res.result(
1231 boost::beast::http::status::internal_server_error);
1232 return;
1233 }
1234
1235 asyncResp->res.result(boost::beast::http::status::ok);
1236 };
1237
1238 // Make call to Logging service to request Delete Log
1239 crow::connections::systemBus->async_method_call(
1240 respHandler, "xyz.openbmc_project.Logging",
1241 "/xyz/openbmc_project/logging/entry/" + entryID,
1242 "xyz.openbmc_project.Object.Delete", "Delete");
1243 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001244};
1245
1246class BMCLogServiceCollection : public Node
1247{
1248 public:
1249 template <typename CrowApp>
1250 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001251 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001252 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001253 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001254 {boost::beast::http::verb::get, {{"Login"}}},
1255 {boost::beast::http::verb::head, {{"Login"}}},
1256 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1257 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1258 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1259 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001260 }
1261
1262 private:
1263 /**
1264 * Functions triggers appropriate requests on DBus
1265 */
1266 void doGet(crow::Response &res, const crow::Request &req,
1267 const std::vector<std::string> &params) override
1268 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001269 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001270 // Collections don't include the static data added by SubRoute because
1271 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001272 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001273 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001274 asyncResp->res.jsonValue["@odata.id"] =
1275 "/redfish/v1/Managers/bmc/LogServices";
1276 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1277 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001278 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001279 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1280 logServiceArray = nlohmann::json::array();
1281#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1282 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001283 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001284#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001285 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001286 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001287 }
1288};
1289
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001290class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001291{
1292 public:
1293 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001294 BMCJournalLogService(CrowApp &app) :
1295 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001296 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001297 entityPrivileges = {
1298 {boost::beast::http::verb::get, {{"Login"}}},
1299 {boost::beast::http::verb::head, {{"Login"}}},
1300 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1301 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1302 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1303 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1304 }
1305
1306 private:
1307 void doGet(crow::Response &res, const crow::Request &req,
1308 const std::vector<std::string> &params) override
1309 {
1310 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001311 asyncResp->res.jsonValue["@odata.type"] =
1312 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001313 asyncResp->res.jsonValue["@odata.id"] =
1314 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001315 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1316 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1317 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001318 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001319 asyncResp->res.jsonValue["Entries"] = {
1320 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001321 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001322 }
1323};
1324
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001325static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1326 sd_journal *journal,
1327 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001328{
1329 // Get the Log Entry contents
1330 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001331
Ed Tanous39e77502019-03-04 17:35:53 -08001332 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001333 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001334 if (ret < 0)
1335 {
1336 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1337 return 1;
1338 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001339
1340 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001341 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001342 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001343 if (ret < 0)
1344 {
1345 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001346 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001347
1348 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001349 std::string entryTimeStr;
1350 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001351 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001352 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001353 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001354
1355 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001356 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001357 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001358 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1359 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001360 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001361 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001362 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001363 {"EntryType", "Oem"},
1364 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001365 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001366 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001367 {"Created", std::move(entryTimeStr)}};
1368 return 0;
1369}
1370
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001371class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001372{
1373 public:
1374 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001375 BMCJournalLogEntryCollection(CrowApp &app) :
1376 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001377 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001378 entityPrivileges = {
1379 {boost::beast::http::verb::get, {{"Login"}}},
1380 {boost::beast::http::verb::head, {{"Login"}}},
1381 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1382 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1383 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1384 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1385 }
1386
1387 private:
1388 void doGet(crow::Response &res, const crow::Request &req,
1389 const std::vector<std::string> &params) override
1390 {
1391 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001392 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001393 uint64_t skip = 0;
1394 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001395 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001396 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001397 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001398 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001399 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001400 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001401 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001402 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001403 // Collections don't include the static data added by SubRoute because
1404 // it has a duplicate entry for members
1405 asyncResp->res.jsonValue["@odata.type"] =
1406 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001407 asyncResp->res.jsonValue["@odata.id"] =
1408 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001409 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001410 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001411 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1412 asyncResp->res.jsonValue["Description"] =
1413 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001414 asyncResp->res.jsonValue["@odata.id"] =
1415 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001416 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1417 logEntryArray = nlohmann::json::array();
1418
1419 // Go through the journal and use the timestamp to create a unique ID
1420 // for each entry
1421 sd_journal *journalTmp = nullptr;
1422 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1423 if (ret < 0)
1424 {
1425 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001426 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001427 return;
1428 }
1429 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1430 journalTmp, sd_journal_close);
1431 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001432 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001433 // Reset the unique ID on the first entry
1434 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001435 SD_JOURNAL_FOREACH(journal.get())
1436 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001437 entryCount++;
1438 // Handle paging using skip (number of entries to skip from the
1439 // start) and top (number of entries to display)
1440 if (entryCount <= skip || entryCount > skip + top)
1441 {
1442 continue;
1443 }
1444
Jason M. Bills16428a12018-11-02 12:42:29 -07001445 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001446 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001447 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001448 continue;
1449 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001450
Jason M. Billse85d6b12019-07-29 17:01:15 -07001451 if (firstEntry)
1452 {
1453 firstEntry = false;
1454 }
1455
Jason M. Billse1f26342018-07-18 12:12:00 -07001456 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001457 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1458 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1459 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001460 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001461 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001462 return;
1463 }
1464 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001465 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1466 if (skip + top < entryCount)
1467 {
1468 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001469 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001470 std::to_string(skip + top);
1471 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001472 }
1473};
1474
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001475class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001476{
1477 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001478 BMCJournalLogEntry(CrowApp &app) :
1479 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001480 std::string())
1481 {
1482 entityPrivileges = {
1483 {boost::beast::http::verb::get, {{"Login"}}},
1484 {boost::beast::http::verb::head, {{"Login"}}},
1485 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1486 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1487 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1488 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1489 }
1490
1491 private:
1492 void doGet(crow::Response &res, const crow::Request &req,
1493 const std::vector<std::string> &params) override
1494 {
1495 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1496 if (params.size() != 1)
1497 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001498 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001499 return;
1500 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001501 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001502 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001503 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001504 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001505 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001506 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001507 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001508 }
1509
1510 sd_journal *journalTmp = nullptr;
1511 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1512 if (ret < 0)
1513 {
1514 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001515 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001516 return;
1517 }
1518 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1519 journalTmp, sd_journal_close);
1520 journalTmp = nullptr;
1521 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001522 // tracking the unique ID
1523 std::string idStr;
1524 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001525 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001526 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001527 {
1528 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001529 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1530 {
1531 messages::internalError(asyncResp->res);
1532 return;
1533 }
1534 if (firstEntry)
1535 {
1536 firstEntry = false;
1537 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001538 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001539 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001540 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001541 {
1542 messages::resourceMissingAtURI(asyncResp->res, entryID);
1543 return;
1544 }
1545
1546 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1547 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001548 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001549 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001550 return;
1551 }
1552 }
1553};
1554
raviteja-bc9bb6862020-02-03 11:53:32 -06001555class SystemDumpService : public Node
1556{
1557 public:
1558 template <typename CrowApp>
1559 SystemDumpService(CrowApp &app) :
1560 Node(app, "/redfish/v1/Systems/system/LogServices/System/")
1561 {
1562 entityPrivileges = {
1563 {boost::beast::http::verb::get, {{"Login"}}},
1564 {boost::beast::http::verb::head, {{"Login"}}},
1565 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1566 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1567 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1568 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1569 }
1570
1571 private:
1572 void doGet(crow::Response &res, const crow::Request &req,
1573 const std::vector<std::string> &params) override
1574 {
1575 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1576
1577 asyncResp->res.jsonValue["@odata.id"] =
1578 "/redfish/v1/Systems/system/LogServices/System";
1579 asyncResp->res.jsonValue["@odata.type"] =
1580 "#LogService.v1_1_0.LogService";
1581 asyncResp->res.jsonValue["Name"] = "Dump Log Service";
1582 asyncResp->res.jsonValue["Description"] = "System Dump Log Service";
1583 asyncResp->res.jsonValue["Id"] = "System";
1584 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1585 asyncResp->res.jsonValue["LogEntryTypes"] = "Dump";
1586 asyncResp->res.jsonValue["Oem"]["DumpType"] = "System";
1587
1588 asyncResp->res.jsonValue["Entries"] = {
1589 {"@odata.id",
1590 "/redfish/v1/Systems/system/LogServices/System/Entries"}};
1591 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1592 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1593 "Actions/LogService.ClearLog"}};
1594 asyncResp->res.jsonValue["Actions"]["#LogService.CreateLog"] = {
1595 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1596 "Actions/LogService.CreateLog"}};
1597 }
1598};
1599
1600class SystemDumpEntryCollection : public Node
1601{
1602 public:
1603 template <typename CrowApp>
1604 SystemDumpEntryCollection(CrowApp &app) :
1605 Node(app, "/redfish/v1/Systems/system/LogServices/System/Entries/")
1606 {
1607 entityPrivileges = {
1608 {boost::beast::http::verb::get, {{"Login"}}},
1609 {boost::beast::http::verb::head, {{"Login"}}},
1610 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1611 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1612 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1613 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1614 }
1615
1616 private:
1617 /**
1618 * Functions triggers appropriate requests on DBus
1619 */
1620 void doGet(crow::Response &res, const crow::Request &req,
1621 const std::vector<std::string> &params) override
1622 {
1623 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1624
1625 asyncResp->res.jsonValue["@odata.type"] =
1626 "#LogEntryCollection.LogEntryCollection";
1627 asyncResp->res.jsonValue["@odata.id"] =
1628 "/redfish/v1/Systems/system/LogServices/System/Entries";
1629 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
1630 asyncResp->res.jsonValue["Description"] =
1631 "Collection of System Dump Entries";
1632
1633 crow::connections::systemBus->async_method_call(
1634 [asyncResp](const boost::system::error_code ec,
1635 const crow::openbmc_mapper::GetSubTreeType &resp) {
1636 if (ec)
1637 {
1638 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1639 messages::internalError(asyncResp->res);
1640 return;
1641 }
1642
1643 nlohmann::json &logArray = asyncResp->res.jsonValue["Members"];
1644 logArray = nlohmann::json::array();
1645 for (auto &object : resp)
1646 {
1647 const std::string &path =
1648 static_cast<const std::string &>(object.first);
1649 std::size_t lastPos = path.rfind("/");
1650 if (lastPos == std::string::npos)
1651 {
1652 continue;
1653 }
1654 std::string logID = path.substr(lastPos + 1);
1655 logArray.push_back(
1656 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1657 "System/Entries/" +
1658 logID}});
1659 }
1660 asyncResp->res.jsonValue["Members@odata.count"] =
1661 logArray.size();
1662 },
1663 "xyz.openbmc_project.ObjectMapper",
1664 "/xyz/openbmc_project/object_mapper",
1665 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1666 "/xyz/openbmc_project/dump", 0,
1667 std::array<const char *, 1>{
1668 "xyz.openbmc_project.Dump.Entry.System"});
1669 }
1670};
1671
1672class SystemDumpEntry : public Node
1673{
1674 public:
1675 SystemDumpEntry(CrowApp &app) :
1676 Node(app,
1677 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/",
1678 std::string())
1679 {
1680 entityPrivileges = {
1681 {boost::beast::http::verb::get, {{"Login"}}},
1682 {boost::beast::http::verb::head, {{"Login"}}},
1683 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1684 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1685 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1686 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1687 }
1688
1689 private:
1690 void doGet(crow::Response &res, const crow::Request &req,
1691 const std::vector<std::string> &params) override
1692 {
1693 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1694 if (params.size() != 1)
1695 {
1696 messages::internalError(asyncResp->res);
1697 return;
1698 }
1699 const std::string &entryID = params[0];
1700 crow::connections::systemBus->async_method_call(
1701 [asyncResp, entryID](const boost::system::error_code ec,
1702 GetManagedObjectsType &resp) {
1703 if (ec)
1704 {
1705 BMCWEB_LOG_ERROR
1706 << "SystemDumpEntry resp_handler got error " << ec;
1707 messages::internalError(asyncResp->res);
1708 return;
1709 }
1710
1711 for (auto &objectPath : resp)
1712 {
1713 if (objectPath.first.str.find(
1714 "/xyz/openbmc_project/dump/entry/" + entryID) ==
1715 std::string::npos)
1716 {
1717 continue;
1718 }
1719
1720 bool foundSystemDumpEntry = false;
1721 for (auto &interfaceMap : objectPath.second)
1722 {
1723 if (interfaceMap.first ==
1724 "xyz.openbmc_project.Dump.Entry.System")
1725 {
1726 foundSystemDumpEntry = true;
1727 break;
1728 }
1729 }
1730 if (foundSystemDumpEntry == false)
1731 {
1732 BMCWEB_LOG_DEBUG << "Can't find System Dump Entry";
1733 messages::internalError(asyncResp->res);
1734 return;
1735 }
1736
1737 std::string timestamp{};
1738 uint64_t size = 0;
1739
1740 for (auto &interfaceMap : objectPath.second)
1741 {
1742 if (interfaceMap.first ==
1743 "xyz.openbmc_project.Dump.Entry")
1744 {
1745 for (auto &propertyMap : interfaceMap.second)
1746 {
1747 if (propertyMap.first == "Size")
1748 {
1749 auto sizePtr = std::get_if<uint64_t>(
1750 &propertyMap.second);
1751 if (sizePtr == nullptr)
1752 {
1753 messages::propertyMissing(
1754 asyncResp->res, "Size");
1755 break;
1756 }
1757 size = *sizePtr;
1758 break;
1759 }
1760 }
1761 }
1762 else if (interfaceMap.first ==
1763 "xyz.openbmc_project.Time.EpochTime")
1764 {
1765 for (auto &propertyMap : interfaceMap.second)
1766 {
1767 if (propertyMap.first == "Elapsed")
1768 {
1769 const uint64_t *usecsTimeStamp =
1770 std::get_if<uint64_t>(
1771 &propertyMap.second);
1772 if (usecsTimeStamp == nullptr)
1773 {
1774 messages::propertyMissing(
1775 asyncResp->res, "Elapsed");
1776 break;
1777 }
1778 getTimestampStr(*usecsTimeStamp, timestamp);
1779 break;
1780 }
1781 }
1782 }
1783 }
1784 asyncResp->res.jsonValue = {
1785 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1786 {"@odata.id",
1787 "/redfish/v1/Systems/system/LogServices/System/"
1788 "Entries/" +
1789 entryID},
1790 {"Name", "System Dump Entry"},
1791 {"Id", entryID},
1792 {"SizeInB", size},
1793 {"EntryType", "Dump"},
1794 {"EntryCode", "User generated dump"},
1795 {"Created", timestamp}};
1796
1797 asyncResp->res
1798 .jsonValue["Actions"]["#LogEntry.DownloadLog"] = {
1799 {"target",
1800 "/redfish/v1/Systems/system/LogServices/System/"
1801 "Entries/" +
1802 entryID + "/Actions/LogEntry.DownloadLog"}};
1803 }
1804 },
1805 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
1806 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1807 }
1808
1809 void doDelete(crow::Response &res, const crow::Request &req,
1810 const std::vector<std::string> &params) override
1811 {
1812 BMCWEB_LOG_DEBUG << "Do delete single dump entry";
1813
1814 auto asyncResp = std::make_shared<AsyncResp>(res);
1815
1816 if (params.size() != 1)
1817 {
1818 messages::internalError(asyncResp->res);
1819 return;
1820 }
1821 std::string entryID = params[0];
1822
1823 crow::connections::systemBus->async_method_call(
1824 [asyncResp,
1825 entryID](const boost::system::error_code ec,
1826 const crow::openbmc_mapper::GetSubTreeType &resp) {
1827 if (ec)
1828 {
1829 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1830 messages::internalError(asyncResp->res);
1831 return;
1832 }
1833
1834 for (auto &object : resp)
1835 {
1836 const std::string &path =
1837 static_cast<const std::string &>(object.first);
1838
1839 std::size_t pos = path.rfind(
1840 "/xyz/openbmc_project/dump/entry/" + entryID);
1841 if (pos != std::string::npos)
1842 {
1843 deleteSystemDumpEntry(asyncResp->res, entryID);
1844 return;
1845 }
1846 }
1847 },
1848 "xyz.openbmc_project.ObjectMapper",
1849 "/xyz/openbmc_project/object_mapper",
1850 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1851 "/xyz/openbmc_project/dump", 0,
1852 std::array<const char *, 1>{
1853 "xyz.openbmc_project.Dump.Entry.System"});
1854 }
1855};
1856
Jason M. Bills424c4172019-03-21 13:50:33 -07001857class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001858{
1859 public:
1860 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001861 CrashdumpService(CrowApp &app) :
1862 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001863 {
AppaRao Puli39460282020-04-07 17:03:04 +05301864 // Note: Deviated from redfish privilege registry for GET & HEAD
1865 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001866 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301867 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1868 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001869 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1870 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1871 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1872 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001873 }
1874
1875 private:
1876 /**
1877 * Functions triggers appropriate requests on DBus
1878 */
1879 void doGet(crow::Response &res, const crow::Request &req,
1880 const std::vector<std::string> &params) override
1881 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001882 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001883 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001884 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001885 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001886 asyncResp->res.jsonValue["@odata.type"] =
1887 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001888 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1889 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1890 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001891 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1892 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001893 asyncResp->res.jsonValue["Entries"] = {
1894 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001895 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001896 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001897 {"#LogService.ClearLog",
1898 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1899 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001900 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001901 {{"#Crashdump.OnDemand",
1902 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1903 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001904
1905#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001906 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001907 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001908 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1909 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001910#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001911 }
1912};
1913
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001914class CrashdumpClear : public Node
1915{
1916 public:
1917 CrashdumpClear(CrowApp &app) :
1918 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1919 "LogService.ClearLog/")
1920 {
AppaRao Puli39460282020-04-07 17:03:04 +05301921 // Note: Deviated from redfish privilege registry for GET & HEAD
1922 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001923 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301924 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1925 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001926 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1927 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1928 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1929 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1930 }
1931
1932 private:
1933 void doPost(crow::Response &res, const crow::Request &req,
1934 const std::vector<std::string> &params) override
1935 {
1936 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1937
1938 crow::connections::systemBus->async_method_call(
1939 [asyncResp](const boost::system::error_code ec,
1940 const std::string &resp) {
1941 if (ec)
1942 {
1943 messages::internalError(asyncResp->res);
1944 return;
1945 }
1946 messages::success(asyncResp->res);
1947 },
1948 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
1949 }
1950};
1951
Jason M. Billse855dd22019-10-08 11:37:48 -07001952static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1953 const std::string &logID,
1954 nlohmann::json &logEntryJson)
1955{
Johnathan Mantey043a0532020-03-10 17:15:28 -07001956 auto getStoredLogCallback =
1957 [asyncResp, logID, &logEntryJson](
1958 const boost::system::error_code ec,
1959 const std::vector<std::pair<std::string, VariantType>> &params) {
1960 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001961 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07001962 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1963 if (ec.value() ==
1964 boost::system::linux_error::bad_request_descriptor)
1965 {
1966 messages::resourceNotFound(asyncResp->res, "LogEntry",
1967 logID);
1968 }
1969 else
1970 {
1971 messages::internalError(asyncResp->res);
1972 }
1973 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001974 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001975
Johnathan Mantey043a0532020-03-10 17:15:28 -07001976 std::string timestamp{};
1977 std::string filename{};
1978 std::string logfile{};
1979 ParseCrashdumpParameters(params, filename, timestamp, logfile);
1980
1981 if (filename.empty() || timestamp.empty())
1982 {
1983 messages::resourceMissingAtURI(asyncResp->res, logID);
1984 return;
1985 }
1986
1987 std::string crashdumpURI =
1988 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1989 logID + "/" + filename;
1990 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1991 {"@odata.id", "/redfish/v1/Systems/system/"
1992 "LogServices/Crashdump/Entries/" +
1993 logID},
1994 {"Name", "CPU Crashdump"},
1995 {"Id", logID},
1996 {"EntryType", "Oem"},
1997 {"OemRecordFormat", "Crashdump URI"},
1998 {"Message", std::move(crashdumpURI)},
1999 {"Created", std::move(timestamp)}};
2000 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002001 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002002 std::move(getStoredLogCallback), crashdumpObject,
2003 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002004 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002005}
2006
Jason M. Bills424c4172019-03-21 13:50:33 -07002007class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002008{
2009 public:
2010 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07002011 CrashdumpEntryCollection(CrowApp &app) :
2012 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002013 {
AppaRao Puli39460282020-04-07 17:03:04 +05302014 // Note: Deviated from redfish privilege registry for GET & HEAD
2015 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002016 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302017 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2018 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002019 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2020 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2021 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2022 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002023 }
2024
2025 private:
2026 /**
2027 * Functions triggers appropriate requests on DBus
2028 */
2029 void doGet(crow::Response &res, const crow::Request &req,
2030 const std::vector<std::string> &params) override
2031 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002032 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002033 // Collections don't include the static data added by SubRoute because
2034 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002035 auto getLogEntriesCallback = [asyncResp](
2036 const boost::system::error_code ec,
2037 const std::vector<std::string> &resp) {
2038 if (ec)
2039 {
2040 if (ec.value() !=
2041 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002042 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002043 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2044 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002045 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002046 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002047 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002048 }
2049 asyncResp->res.jsonValue["@odata.type"] =
2050 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002051 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002052 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002053 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002054 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002055 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002056 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
2057 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002058 std::vector<std::string> logIDs;
2059 // Get the list of log entries and build up an empty array big
2060 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07002061 for (const std::string &objpath : resp)
2062 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002063 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002064 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002065 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002066 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002067 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002068 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002069 logIDs.emplace_back(objpath.substr(lastPos + 1));
2070
2071 // Add a space for the log entry to the array
2072 logEntryArray.push_back({});
2073 }
2074 // Now go through and set up async calls to fill in the entries
2075 size_t index = 0;
2076 for (const std::string &logID : logIDs)
2077 {
2078 // Add the log entry to the array
2079 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002080 }
2081 asyncResp->res.jsonValue["Members@odata.count"] =
2082 logEntryArray.size();
2083 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002084 crow::connections::systemBus->async_method_call(
2085 std::move(getLogEntriesCallback),
2086 "xyz.openbmc_project.ObjectMapper",
2087 "/xyz/openbmc_project/object_mapper",
2088 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002089 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002090 }
2091};
2092
Jason M. Bills424c4172019-03-21 13:50:33 -07002093class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002094{
2095 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002096 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002097 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002098 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002099 std::string())
2100 {
AppaRao Puli39460282020-04-07 17:03:04 +05302101 // Note: Deviated from redfish privilege registry for GET & HEAD
2102 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002103 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302104 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2105 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002106 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2107 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2108 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2109 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002110 }
2111
2112 private:
2113 void doGet(crow::Response &res, const crow::Request &req,
2114 const std::vector<std::string> &params) override
2115 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002116 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002117 if (params.size() != 1)
2118 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002119 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002120 return;
2121 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002122 const std::string &logID = params[0];
2123 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2124 }
2125};
2126
2127class CrashdumpFile : public Node
2128{
2129 public:
2130 CrashdumpFile(CrowApp &app) :
2131 Node(app,
2132 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2133 "<str>/",
2134 std::string(), std::string())
2135 {
AppaRao Puli39460282020-04-07 17:03:04 +05302136 // Note: Deviated from redfish privilege registry for GET & HEAD
2137 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002138 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302139 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2140 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002141 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2142 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2143 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2144 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2145 }
2146
2147 private:
2148 void doGet(crow::Response &res, const crow::Request &req,
2149 const std::vector<std::string> &params) override
2150 {
2151 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2152 if (params.size() != 2)
2153 {
2154 messages::internalError(asyncResp->res);
2155 return;
2156 }
2157 const std::string &logID = params[0];
2158 const std::string &fileName = params[1];
2159
Johnathan Mantey043a0532020-03-10 17:15:28 -07002160 auto getStoredLogCallback =
2161 [asyncResp, logID, fileName](
2162 const boost::system::error_code ec,
2163 const std::vector<std::pair<std::string, VariantType>> &resp) {
2164 if (ec)
2165 {
2166 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2167 << ec.message();
2168 messages::internalError(asyncResp->res);
2169 return;
2170 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002171
Johnathan Mantey043a0532020-03-10 17:15:28 -07002172 std::string dbusFilename{};
2173 std::string dbusTimestamp{};
2174 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002175
Johnathan Mantey043a0532020-03-10 17:15:28 -07002176 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2177 dbusFilepath);
2178
2179 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2180 dbusFilepath.empty())
2181 {
2182 messages::resourceMissingAtURI(asyncResp->res, fileName);
2183 return;
2184 }
2185
2186 // Verify the file name parameter is correct
2187 if (fileName != dbusFilename)
2188 {
2189 messages::resourceMissingAtURI(asyncResp->res, fileName);
2190 return;
2191 }
2192
2193 if (!std::filesystem::exists(dbusFilepath))
2194 {
2195 messages::resourceMissingAtURI(asyncResp->res, fileName);
2196 return;
2197 }
2198 std::ifstream ifs(dbusFilepath, std::ios::in |
2199 std::ios::binary |
2200 std::ios::ate);
2201 std::ifstream::pos_type fileSize = ifs.tellg();
2202 if (fileSize < 0)
2203 {
2204 messages::generalError(asyncResp->res);
2205 return;
2206 }
2207 ifs.seekg(0, std::ios::beg);
2208
2209 auto crashData = std::make_unique<char[]>(
2210 static_cast<unsigned int>(fileSize));
2211
2212 ifs.read(crashData.get(), static_cast<int>(fileSize));
2213
2214 // The cast to std::string is intentional in order to use the
2215 // assign() that applies move mechanics
2216 asyncResp->res.body().assign(
2217 static_cast<std::string>(crashData.get()));
2218
2219 // Configure this to be a file download when accessed from
2220 // a browser
2221 asyncResp->res.addHeader("Content-Disposition", "attachment");
2222 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002223 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002224 std::move(getStoredLogCallback), crashdumpObject,
2225 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002226 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002227 }
2228};
2229
Jason M. Bills424c4172019-03-21 13:50:33 -07002230class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002231{
2232 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002233 OnDemandCrashdump(CrowApp &app) :
2234 Node(app,
2235 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2236 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002237 {
AppaRao Puli39460282020-04-07 17:03:04 +05302238 // Note: Deviated from redfish privilege registry for GET & HEAD
2239 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002240 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302241 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2242 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2243 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2244 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2245 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2246 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002247 }
2248
2249 private:
2250 void doPost(crow::Response &res, const crow::Request &req,
2251 const std::vector<std::string> &params) override
2252 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002253 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002254
James Feistfe306722020-03-12 16:32:08 -07002255 auto generateonDemandLogCallback = [asyncResp,
2256 req](const boost::system::error_code
2257 ec,
2258 const std::string &resp) {
James Feist46229572020-02-19 15:11:58 -08002259 if (ec)
2260 {
2261 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002262 {
James Feist46229572020-02-19 15:11:58 -08002263 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002264 }
James Feist46229572020-02-19 15:11:58 -08002265 else if (ec.value() ==
2266 boost::system::errc::device_or_resource_busy)
2267 {
2268 messages::serviceTemporarilyUnavailable(asyncResp->res,
2269 "60");
2270 }
2271 else
2272 {
2273 messages::internalError(asyncResp->res);
2274 }
2275 return;
2276 }
2277 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08002278 [](boost::system::error_code err, sdbusplus::message::message &,
2279 const std::shared_ptr<task::TaskData> &taskData) {
2280 if (!err)
2281 {
2282 taskData->messages.emplace_back(messages::success());
James Feist831d6b02020-03-12 16:31:30 -07002283 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002284 }
James Feist32898ce2020-03-10 16:16:52 -07002285 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002286 },
James Feist46229572020-02-19 15:11:58 -08002287 "type='signal',interface='org.freedesktop.DBus.Properties',"
2288 "member='PropertiesChanged',arg0namespace='com.intel."
2289 "crashdump'");
2290 task->startTimer(std::chrono::minutes(5));
2291 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002292 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002293 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002294 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002295 std::move(generateonDemandLogCallback), crashdumpObject,
2296 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002297 }
2298};
2299
Jason M. Billse1f26342018-07-18 12:12:00 -07002300class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002301{
2302 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07002303 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002304 Node(app,
2305 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2306 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002307 {
AppaRao Puli39460282020-04-07 17:03:04 +05302308 // Note: Deviated from redfish privilege registry for GET & HEAD
2309 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002310 entityPrivileges = {
2311 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2312 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2313 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2314 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2315 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2316 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2317 }
2318
2319 private:
2320 void doPost(crow::Response &res, const crow::Request &req,
2321 const std::vector<std::string> &params) override
2322 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002323 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002324 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002325
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002326 nlohmann::json reqJson =
2327 nlohmann::json::parse(req.body, nullptr, false);
2328 if (reqJson.find("PECICommands") != reqJson.end())
2329 {
2330 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2331 {
2332 return;
2333 }
2334 uint32_t idx = 0;
2335 for (auto const &cmd : peciCommands)
2336 {
2337 if (cmd.size() < 3)
2338 {
2339 std::string s("[");
2340 for (auto const &val : cmd)
2341 {
2342 if (val != *cmd.begin())
2343 {
2344 s += ",";
2345 }
2346 s += std::to_string(val);
2347 }
2348 s += "]";
2349 messages::actionParameterValueFormatError(
2350 res, s, "PECICommands[" + std::to_string(idx) + "]",
2351 "SendRawPeci");
2352 return;
2353 }
2354 idx++;
2355 }
2356 }
2357 else
2358 {
2359 /* This interface is deprecated */
2360 uint8_t clientAddress = 0;
2361 uint8_t readLength = 0;
2362 std::vector<uint8_t> peciCommand;
2363 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2364 "ReadLength", readLength, "PECICommand",
2365 peciCommand))
2366 {
2367 return;
2368 }
2369 peciCommands.push_back({clientAddress, 0, readLength});
2370 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2371 peciCommand.end());
2372 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002373 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002374 auto sendRawPECICallback =
2375 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002376 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002377 if (ec)
2378 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002379 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002380 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002381 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002382 return;
2383 }
2384 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2385 {"PECIResponse", resp}};
2386 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002387 // Call the SendRawPECI command with the provided data
2388 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002389 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002390 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002391 }
2392};
2393
Andrew Geisslercb92c032018-08-17 07:56:14 -07002394/**
2395 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2396 */
2397class DBusLogServiceActionsClear : public Node
2398{
2399 public:
2400 DBusLogServiceActionsClear(CrowApp &app) :
2401 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002402 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002403 {
2404 entityPrivileges = {
2405 {boost::beast::http::verb::get, {{"Login"}}},
2406 {boost::beast::http::verb::head, {{"Login"}}},
2407 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2408 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2409 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2410 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2411 }
2412
2413 private:
2414 /**
2415 * Function handles POST method request.
2416 * The Clear Log actions does not require any parameter.The action deletes
2417 * all entries found in the Entries collection for this Log Service.
2418 */
2419 void doPost(crow::Response &res, const crow::Request &req,
2420 const std::vector<std::string> &params) override
2421 {
2422 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2423
2424 auto asyncResp = std::make_shared<AsyncResp>(res);
2425 // Process response from Logging service.
2426 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2427 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2428 if (ec)
2429 {
2430 // TODO Handle for specific error code
2431 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2432 asyncResp->res.result(
2433 boost::beast::http::status::internal_server_error);
2434 return;
2435 }
2436
2437 asyncResp->res.result(boost::beast::http::status::no_content);
2438 };
2439
2440 // Make call to Logging service to request Clear Log
2441 crow::connections::systemBus->async_method_call(
2442 resp_handler, "xyz.openbmc_project.Logging",
2443 "/xyz/openbmc_project/logging",
2444 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2445 }
2446};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002447
2448/****************************************************
2449 * Redfish PostCode interfaces
2450 * using DBUS interface: getPostCodesTS
2451 ******************************************************/
2452class PostCodesLogService : public Node
2453{
2454 public:
2455 PostCodesLogService(CrowApp &app) :
2456 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2457 {
2458 entityPrivileges = {
2459 {boost::beast::http::verb::get, {{"Login"}}},
2460 {boost::beast::http::verb::head, {{"Login"}}},
2461 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2462 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2463 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2464 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2465 }
2466
2467 private:
2468 void doGet(crow::Response &res, const crow::Request &req,
2469 const std::vector<std::string> &params) override
2470 {
2471 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2472
2473 asyncResp->res.jsonValue = {
2474 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2475 {"@odata.type", "#LogService.v1_1_0.LogService"},
2476 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2477 {"Name", "POST Code Log Service"},
2478 {"Description", "POST Code Log Service"},
2479 {"Id", "BIOS POST Code Log"},
2480 {"OverWritePolicy", "WrapsWhenFull"},
2481 {"Entries",
2482 {{"@odata.id",
2483 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2484 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2485 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2486 "Actions/LogService.ClearLog"}};
2487 }
2488};
2489
2490class PostCodesClear : public Node
2491{
2492 public:
2493 PostCodesClear(CrowApp &app) :
2494 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2495 "LogService.ClearLog/")
2496 {
2497 entityPrivileges = {
2498 {boost::beast::http::verb::get, {{"Login"}}},
2499 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302500 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2501 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2502 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2503 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002504 }
2505
2506 private:
2507 void doPost(crow::Response &res, const crow::Request &req,
2508 const std::vector<std::string> &params) override
2509 {
2510 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2511
2512 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2513 // Make call to post-code service to request clear all
2514 crow::connections::systemBus->async_method_call(
2515 [asyncResp](const boost::system::error_code ec) {
2516 if (ec)
2517 {
2518 // TODO Handle for specific error code
2519 BMCWEB_LOG_ERROR
2520 << "doClearPostCodes resp_handler got error " << ec;
2521 asyncResp->res.result(
2522 boost::beast::http::status::internal_server_error);
2523 messages::internalError(asyncResp->res);
2524 return;
2525 }
2526 },
2527 "xyz.openbmc_project.State.Boot.PostCode",
2528 "/xyz/openbmc_project/State/Boot/PostCode",
2529 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2530 }
2531};
2532
2533static void fillPostCodeEntry(
2534 std::shared_ptr<AsyncResp> aResp,
2535 const boost::container::flat_map<uint64_t, uint64_t> &postcode,
2536 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2537 const uint64_t skip = 0, const uint64_t top = 0)
2538{
2539 // Get the Message from the MessageRegistry
2540 const message_registries::Message *message =
2541 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
2542 std::string severity;
2543 if (message != nullptr)
2544 {
2545 severity = message->severity;
2546 }
2547
2548 uint64_t currentCodeIndex = 0;
2549 nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"];
2550
2551 uint64_t firstCodeTimeUs = 0;
2552 for (const std::pair<uint64_t, uint64_t> &code : postcode)
2553 {
2554 currentCodeIndex++;
2555 std::string postcodeEntryID =
2556 "B" + std::to_string(bootIndex) + "-" +
2557 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2558
2559 uint64_t usecSinceEpoch = code.first;
2560 uint64_t usTimeOffset = 0;
2561
2562 if (1 == currentCodeIndex)
2563 { // already incremented
2564 firstCodeTimeUs = code.first;
2565 }
2566 else
2567 {
2568 usTimeOffset = code.first - firstCodeTimeUs;
2569 }
2570
2571 // skip if no specific codeIndex is specified and currentCodeIndex does
2572 // not fall between top and skip
2573 if ((codeIndex == 0) &&
2574 (currentCodeIndex <= skip || currentCodeIndex > top))
2575 {
2576 continue;
2577 }
2578
2579 // skip if a sepcific codeIndex is specified and does not match the
2580 // currentIndex
2581 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2582 {
2583 // This is done for simplicity. 1st entry is needed to calculate
2584 // time offset. To improve efficiency, one can get to the entry
2585 // directly (possibly with flatmap's nth method)
2586 continue;
2587 }
2588
2589 // currentCodeIndex is within top and skip or equal to specified code
2590 // index
2591
2592 // Get the Created time from the timestamp
2593 std::string entryTimeStr;
2594 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2595 {
2596 continue;
2597 }
2598
2599 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2600 std::ostringstream hexCode;
2601 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2602 << code.second;
2603 std::ostringstream timeOffsetStr;
2604 // Set Fixed -Point Notation
2605 timeOffsetStr << std::fixed;
2606 // Set precision to 4 digits
2607 timeOffsetStr << std::setprecision(4);
2608 // Add double to stream
2609 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2610 std::vector<std::string> messageArgs = {
2611 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2612
2613 // Get MessageArgs template from message registry
2614 std::string msg;
2615 if (message != nullptr)
2616 {
2617 msg = message->message;
2618
2619 // fill in this post code value
2620 int i = 0;
2621 for (const std::string &messageArg : messageArgs)
2622 {
2623 std::string argStr = "%" + std::to_string(++i);
2624 size_t argPos = msg.find(argStr);
2625 if (argPos != std::string::npos)
2626 {
2627 msg.replace(argPos, argStr.length(), messageArg);
2628 }
2629 }
2630 }
2631
2632 // add to AsyncResp
2633 logEntryArray.push_back({});
2634 nlohmann::json &bmcLogEntry = logEntryArray.back();
2635 bmcLogEntry = {
2636 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2637 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2638 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2639 "PostCodes/Entries/" +
2640 postcodeEntryID},
2641 {"Name", "POST Code Log Entry"},
2642 {"Id", postcodeEntryID},
2643 {"Message", std::move(msg)},
2644 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2645 {"MessageArgs", std::move(messageArgs)},
2646 {"EntryType", "Event"},
2647 {"Severity", std::move(severity)},
2648 {"Created", std::move(entryTimeStr)}};
2649 }
2650}
2651
2652static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2653 const uint16_t bootIndex,
2654 const uint64_t codeIndex)
2655{
2656 crow::connections::systemBus->async_method_call(
2657 [aResp, bootIndex, codeIndex](
2658 const boost::system::error_code ec,
2659 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2660 if (ec)
2661 {
2662 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2663 messages::internalError(aResp->res);
2664 return;
2665 }
2666
2667 // skip the empty postcode boots
2668 if (postcode.empty())
2669 {
2670 return;
2671 }
2672
2673 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2674
2675 aResp->res.jsonValue["Members@odata.count"] =
2676 aResp->res.jsonValue["Members"].size();
2677 },
2678 "xyz.openbmc_project.State.Boot.PostCode",
2679 "/xyz/openbmc_project/State/Boot/PostCode",
2680 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2681 bootIndex);
2682}
2683
2684static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2685 const uint16_t bootIndex,
2686 const uint16_t bootCount,
2687 const uint64_t entryCount, const uint64_t skip,
2688 const uint64_t top)
2689{
2690 crow::connections::systemBus->async_method_call(
2691 [aResp, bootIndex, bootCount, entryCount, skip,
2692 top](const boost::system::error_code ec,
2693 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2694 if (ec)
2695 {
2696 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2697 messages::internalError(aResp->res);
2698 return;
2699 }
2700
2701 uint64_t endCount = entryCount;
2702 if (!postcode.empty())
2703 {
2704 endCount = entryCount + postcode.size();
2705
2706 if ((skip < endCount) && ((top + skip) > entryCount))
2707 {
2708 uint64_t thisBootSkip =
2709 std::max(skip, entryCount) - entryCount;
2710 uint64_t thisBootTop =
2711 std::min(top + skip, endCount) - entryCount;
2712
2713 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2714 thisBootSkip, thisBootTop);
2715 }
2716 aResp->res.jsonValue["Members@odata.count"] = endCount;
2717 }
2718
2719 // continue to previous bootIndex
2720 if (bootIndex < bootCount)
2721 {
2722 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2723 bootCount, endCount, skip, top);
2724 }
2725 else
2726 {
2727 aResp->res.jsonValue["Members@odata.nextLink"] =
2728 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2729 "Entries?$skip=" +
2730 std::to_string(skip + top);
2731 }
2732 },
2733 "xyz.openbmc_project.State.Boot.PostCode",
2734 "/xyz/openbmc_project/State/Boot/PostCode",
2735 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2736 bootIndex);
2737}
2738
2739static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2740 const uint64_t skip, const uint64_t top)
2741{
2742 uint64_t entryCount = 0;
2743 crow::connections::systemBus->async_method_call(
2744 [aResp, entryCount, skip,
2745 top](const boost::system::error_code ec,
2746 const std::variant<uint16_t> &bootCount) {
2747 if (ec)
2748 {
2749 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2750 messages::internalError(aResp->res);
2751 return;
2752 }
2753 auto pVal = std::get_if<uint16_t>(&bootCount);
2754 if (pVal)
2755 {
2756 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2757 }
2758 else
2759 {
2760 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2761 }
2762 },
2763 "xyz.openbmc_project.State.Boot.PostCode",
2764 "/xyz/openbmc_project/State/Boot/PostCode",
2765 "org.freedesktop.DBus.Properties", "Get",
2766 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2767}
2768
2769class PostCodesEntryCollection : public Node
2770{
2771 public:
2772 template <typename CrowApp>
2773 PostCodesEntryCollection(CrowApp &app) :
2774 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2775 {
2776 entityPrivileges = {
2777 {boost::beast::http::verb::get, {{"Login"}}},
2778 {boost::beast::http::verb::head, {{"Login"}}},
2779 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2780 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2781 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2782 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2783 }
2784
2785 private:
2786 void doGet(crow::Response &res, const crow::Request &req,
2787 const std::vector<std::string> &params) override
2788 {
2789 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2790
2791 asyncResp->res.jsonValue["@odata.type"] =
2792 "#LogEntryCollection.LogEntryCollection";
2793 asyncResp->res.jsonValue["@odata.context"] =
2794 "/redfish/v1/"
2795 "$metadata#LogEntryCollection.LogEntryCollection";
2796 asyncResp->res.jsonValue["@odata.id"] =
2797 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2798 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2799 asyncResp->res.jsonValue["Description"] =
2800 "Collection of POST Code Log Entries";
2801 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2802 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2803
2804 uint64_t skip = 0;
2805 uint64_t top = maxEntriesPerPage; // Show max entries by default
2806 if (!getSkipParam(asyncResp->res, req, skip))
2807 {
2808 return;
2809 }
2810 if (!getTopParam(asyncResp->res, req, top))
2811 {
2812 return;
2813 }
2814 getCurrentBootNumber(asyncResp, skip, top);
2815 }
2816};
2817
2818class PostCodesEntry : public Node
2819{
2820 public:
2821 PostCodesEntry(CrowApp &app) :
2822 Node(app,
2823 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2824 std::string())
2825 {
2826 entityPrivileges = {
2827 {boost::beast::http::verb::get, {{"Login"}}},
2828 {boost::beast::http::verb::head, {{"Login"}}},
2829 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2830 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2831 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2832 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2833 }
2834
2835 private:
2836 void doGet(crow::Response &res, const crow::Request &req,
2837 const std::vector<std::string> &params) override
2838 {
2839 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2840 if (params.size() != 1)
2841 {
2842 messages::internalError(asyncResp->res);
2843 return;
2844 }
2845
2846 const std::string &targetID = params[0];
2847
2848 size_t bootPos = targetID.find('B');
2849 if (bootPos == std::string::npos)
2850 {
2851 // Requested ID was not found
2852 messages::resourceMissingAtURI(asyncResp->res, targetID);
2853 return;
2854 }
2855 std::string_view bootIndexStr(targetID);
2856 bootIndexStr.remove_prefix(bootPos + 1);
2857 uint16_t bootIndex = 0;
2858 uint64_t codeIndex = 0;
2859 size_t dashPos = bootIndexStr.find('-');
2860
2861 if (dashPos == std::string::npos)
2862 {
2863 return;
2864 }
2865 std::string_view codeIndexStr(bootIndexStr);
2866 bootIndexStr.remove_suffix(dashPos);
2867 codeIndexStr.remove_prefix(dashPos + 1);
2868
2869 bootIndex = static_cast<uint16_t>(
2870 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2871 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2872 if (bootIndex == 0 || codeIndex == 0)
2873 {
2874 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2875 << params[0];
2876 }
2877
2878 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2879 asyncResp->res.jsonValue["@odata.context"] =
2880 "/redfish/v1/$metadata#LogEntry.LogEntry";
2881 asyncResp->res.jsonValue["@odata.id"] =
2882 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2883 "Entries";
2884 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2885 asyncResp->res.jsonValue["Description"] =
2886 "Collection of POST Code Log Entries";
2887 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2888 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2889
2890 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2891 }
2892};
2893
Ed Tanous1da66f72018-07-27 16:13:37 -07002894} // namespace redfish