blob: 85903732671273de71789996a3e25450a2419351 [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
raviteja-b06578432020-02-03 12:50:42 -060030#include <dump_offload.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070031#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070032#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070033#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080034#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070035
36namespace redfish
37{
38
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070039constexpr char const *crashdumpObject = "com.intel.crashdump";
40constexpr char const *crashdumpPath = "/com/intel/crashdump";
41constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
42constexpr char const *crashdumpInterface = "com.intel.crashdump";
43constexpr char const *deleteAllInterface =
44 "xyz.openbmc_project.Collection.DeleteAll";
45constexpr char const *crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070046 "com.intel.crashdump.OnDemand";
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070047constexpr char const *crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070048 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070049
Jason M. Bills4851d452019-03-28 11:27:48 -070050namespace message_registries
51{
52static const Message *getMessageFromRegistry(
53 const std::string &messageKey,
54 const boost::beast::span<const MessageEntry> registry)
55{
56 boost::beast::span<const MessageEntry>::const_iterator messageIt =
57 std::find_if(registry.cbegin(), registry.cend(),
58 [&messageKey](const MessageEntry &messageEntry) {
59 return !std::strcmp(messageEntry.first,
60 messageKey.c_str());
61 });
62 if (messageIt != registry.cend())
63 {
64 return &messageIt->second;
65 }
66
67 return nullptr;
68}
69
70static const Message *getMessage(const std::string_view &messageID)
71{
72 // Redfish MessageIds are in the form
73 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
74 // the right Message
75 std::vector<std::string> fields;
76 fields.reserve(4);
77 boost::split(fields, messageID, boost::is_any_of("."));
78 std::string &registryName = fields[0];
79 std::string &messageKey = fields[3];
80
81 // Find the right registry and check it for the MessageKey
82 if (std::string(base::header.registryPrefix) == registryName)
83 {
84 return getMessageFromRegistry(
85 messageKey, boost::beast::span<const MessageEntry>(base::registry));
86 }
87 if (std::string(openbmc::header.registryPrefix) == registryName)
88 {
89 return getMessageFromRegistry(
90 messageKey,
91 boost::beast::span<const MessageEntry>(openbmc::registry));
92 }
93 return nullptr;
94}
95} // namespace message_registries
96
James Feistf6150402019-01-08 10:36:20 -080097namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070098
Andrew Geisslercb92c032018-08-17 07:56:14 -070099using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500100 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
101 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700102
103using GetManagedObjectsType = boost::container::flat_map<
104 sdbusplus::message::object_path,
105 boost::container::flat_map<std::string, GetManagedPropertyType>>;
106
107inline std::string translateSeverityDbusToRedfish(const std::string &s)
108{
109 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
110 {
111 return "Critical";
112 }
113 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
114 {
115 return "Critical";
116 }
117 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
118 {
119 return "OK";
120 }
121 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
122 {
123 return "Critical";
124 }
125 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
126 {
127 return "Critical";
128 }
129 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
130 {
131 return "OK";
132 }
133 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
134 {
135 return "OK";
136 }
137 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
138 {
139 return "Warning";
140 }
141 return "";
142}
143
raviteja-bc9bb6862020-02-03 11:53:32 -0600144inline void deleteSystemDumpEntry(crow::Response &res,
145 const std::string &entryID)
146{
147 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
148
149 auto respHandler = [asyncResp](const boost::system::error_code ec) {
150 BMCWEB_LOG_DEBUG << "System Dump Entry doDelete callback: Done";
151 if (ec)
152 {
153 BMCWEB_LOG_ERROR
154 << "System Dump (DBus) doDelete respHandler got error " << ec;
155 asyncResp->res.result(
156 boost::beast::http::status::internal_server_error);
157 return;
158 }
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 =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500462 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700463 if (value != nullptr)
464 {
465 timestamp = *value;
466 }
467 }
468 else if (property.first == "Filename")
469 {
470 const std::string *value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500471 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700472 if (value != nullptr)
473 {
474 filename = *value;
475 }
476 }
477 else if (property.first == "Log")
478 {
479 const std::string *value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500480 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700481 if (value != nullptr)
482 {
483 logfile = *value;
484 }
485 }
486 }
487}
488
ZhikuiRena3316fc2020-01-29 14:58:08 -0800489constexpr char const *postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800490class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700491{
492 public:
493 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800494 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800495 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800496 {
497 entityPrivileges = {
498 {boost::beast::http::verb::get, {{"Login"}}},
499 {boost::beast::http::verb::head, {{"Login"}}},
500 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
501 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
502 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
503 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
504 }
505
506 private:
507 /**
508 * Functions triggers appropriate requests on DBus
509 */
510 void doGet(crow::Response &res, const crow::Request &req,
511 const std::vector<std::string> &params) override
512 {
513 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800514 // Collections don't include the static data added by SubRoute because
515 // it has a duplicate entry for members
516 asyncResp->res.jsonValue["@odata.type"] =
517 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800518 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800519 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800520 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
521 asyncResp->res.jsonValue["Description"] =
522 "Collection of LogServices for this Computer System";
523 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
524 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800525 logServiceArray.push_back(
526 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600527#ifdef BMCWEB_ENABLE_REDFISH_SYSTEMDUMP_LOG
528 logServiceArray.push_back(
529 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/System"}});
530#endif
531
Jason M. Billsd53dd412019-02-12 17:16:22 -0800532#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
533 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500534 {{"@odata.id",
535 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800536#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800537 asyncResp->res.jsonValue["Members@odata.count"] =
538 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800539
540 crow::connections::systemBus->async_method_call(
541 [asyncResp](const boost::system::error_code ec,
542 const std::vector<std::string> &subtreePath) {
543 if (ec)
544 {
545 BMCWEB_LOG_ERROR << ec;
546 return;
547 }
548
549 for (auto &pathStr : subtreePath)
550 {
551 if (pathStr.find("PostCode") != std::string::npos)
552 {
553 nlohmann::json &logServiceArray =
554 asyncResp->res.jsonValue["Members"];
555 logServiceArray.push_back(
556 {{"@odata.id", "/redfish/v1/Systems/system/"
557 "LogServices/PostCodes"}});
558 asyncResp->res.jsonValue["Members@odata.count"] =
559 logServiceArray.size();
560 return;
561 }
562 }
563 },
564 "xyz.openbmc_project.ObjectMapper",
565 "/xyz/openbmc_project/object_mapper",
566 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
567 std::array<const char *, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800568 }
569};
570
571class EventLogService : public Node
572{
573 public:
574 template <typename CrowApp>
575 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800576 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800577 {
578 entityPrivileges = {
579 {boost::beast::http::verb::get, {{"Login"}}},
580 {boost::beast::http::verb::head, {{"Login"}}},
581 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
582 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
583 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
584 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
585 }
586
587 private:
588 void doGet(crow::Response &res, const crow::Request &req,
589 const std::vector<std::string> &params) override
590 {
591 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
592
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800593 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800594 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800595 asyncResp->res.jsonValue["@odata.type"] =
596 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800597 asyncResp->res.jsonValue["Name"] = "Event Log Service";
598 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -0500599 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800600 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
601 asyncResp->res.jsonValue["Entries"] = {
602 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800603 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500604 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
605
606 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
607 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700608 }
609};
610
Tim Lee1f56a3a2019-10-09 10:17:57 +0800611class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700612{
613 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800614 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700615 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
616 "LogService.ClearLog/")
617 {
618 entityPrivileges = {
619 {boost::beast::http::verb::get, {{"Login"}}},
620 {boost::beast::http::verb::head, {{"Login"}}},
621 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
622 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
623 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
624 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
625 }
626
627 private:
628 void doPost(crow::Response &res, const crow::Request &req,
629 const std::vector<std::string> &params) override
630 {
631 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
632
633 // Clear the EventLog by deleting the log files
634 std::vector<std::filesystem::path> redfishLogFiles;
635 if (getRedfishLogFiles(redfishLogFiles))
636 {
637 for (const std::filesystem::path &file : redfishLogFiles)
638 {
639 std::error_code ec;
640 std::filesystem::remove(file, ec);
641 }
642 }
643
644 // Reload rsyslog so it knows to start new log files
645 crow::connections::systemBus->async_method_call(
646 [asyncResp](const boost::system::error_code ec) {
647 if (ec)
648 {
649 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
650 messages::internalError(asyncResp->res);
651 return;
652 }
653
654 messages::success(asyncResp->res);
655 },
656 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
657 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
658 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800659 }
660};
661
Jason M. Bills95820182019-04-22 16:25:34 -0700662static int fillEventLogEntryJson(const std::string &logEntryID,
663 const std::string logEntry,
664 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800665{
Jason M. Bills95820182019-04-22 16:25:34 -0700666 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700667 // First get the Timestamp
668 size_t space = logEntry.find_first_of(" ");
669 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700670 {
671 return 1;
672 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700673 std::string timestamp = logEntry.substr(0, space);
674 // Then get the log contents
675 size_t entryStart = logEntry.find_first_not_of(" ", space);
676 if (entryStart == std::string::npos)
677 {
678 return 1;
679 }
680 std::string_view entry(logEntry);
681 entry.remove_prefix(entryStart);
682 // Use split to separate the entry into its fields
683 std::vector<std::string> logEntryFields;
684 boost::split(logEntryFields, entry, boost::is_any_of(","),
685 boost::token_compress_on);
686 // We need at least a MessageId to be valid
687 if (logEntryFields.size() < 1)
688 {
689 return 1;
690 }
691 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700692
Jason M. Bills4851d452019-03-28 11:27:48 -0700693 // Get the Message from the MessageRegistry
694 const message_registries::Message *message =
695 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800696
Jason M. Bills4851d452019-03-28 11:27:48 -0700697 std::string msg;
698 std::string severity;
699 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800700 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700701 msg = message->message;
702 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800703 }
704
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700705 // Get the MessageArgs from the log if there are any
706 boost::beast::span<std::string> messageArgs;
707 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700708 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700709 std::string &messageArgsStart = logEntryFields[1];
710 // If the first string is empty, assume there are no MessageArgs
711 std::size_t messageArgsSize = 0;
712 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700713 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700714 messageArgsSize = logEntryFields.size() - 1;
715 }
716
717 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
718
719 // Fill the MessageArgs into the Message
720 int i = 0;
721 for (const std::string &messageArg : messageArgs)
722 {
723 std::string argStr = "%" + std::to_string(++i);
724 size_t argPos = msg.find(argStr);
725 if (argPos != std::string::npos)
726 {
727 msg.replace(argPos, argStr.length(), messageArg);
728 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700729 }
730 }
731
Jason M. Bills95820182019-04-22 16:25:34 -0700732 // Get the Created time from the timestamp. The log timestamp is in RFC3339
733 // format which matches the Redfish format except for the fractional seconds
734 // between the '.' and the '+', so just remove them.
735 std::size_t dot = timestamp.find_first_of(".");
736 std::size_t plus = timestamp.find_first_of("+");
737 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800738 {
Jason M. Bills95820182019-04-22 16:25:34 -0700739 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800740 }
741
742 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700743 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700744 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800745 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700746 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700747 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800748 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700749 {"Id", logEntryID},
750 {"Message", std::move(msg)},
751 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800752 {"MessageArgs", std::move(messageArgs)},
753 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700754 {"Severity", std::move(severity)},
755 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800756 return 0;
757}
758
Anthony Wilson27062602019-04-22 02:10:09 -0500759class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800760{
761 public:
762 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500763 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800764 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800765 {
766 entityPrivileges = {
767 {boost::beast::http::verb::get, {{"Login"}}},
768 {boost::beast::http::verb::head, {{"Login"}}},
769 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
770 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
771 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
772 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
773 }
774
775 private:
776 void doGet(crow::Response &res, const crow::Request &req,
777 const std::vector<std::string> &params) override
778 {
779 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700780 uint64_t skip = 0;
781 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800782 if (!getSkipParam(asyncResp->res, req, skip))
783 {
784 return;
785 }
786 if (!getTopParam(asyncResp->res, req, top))
787 {
788 return;
789 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800790 // Collections don't include the static data added by SubRoute because
791 // it has a duplicate entry for members
792 asyncResp->res.jsonValue["@odata.type"] =
793 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800794 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800795 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800796 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
797 asyncResp->res.jsonValue["Description"] =
798 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700799
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800800 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
801 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700802 // Go through the log files and create a unique ID for each entry
803 std::vector<std::filesystem::path> redfishLogFiles;
804 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000805 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700806 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700807
808 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700809 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
810 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800811 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700812 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700813 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800814 {
815 continue;
816 }
817
Jason M. Billse85d6b12019-07-29 17:01:15 -0700818 // Reset the unique ID on the first entry
819 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700820 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800821 {
Jason M. Bills95820182019-04-22 16:25:34 -0700822 entryCount++;
823 // Handle paging using skip (number of entries to skip from the
824 // start) and top (number of entries to display)
825 if (entryCount <= skip || entryCount > skip + top)
826 {
827 continue;
828 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800829
Jason M. Bills95820182019-04-22 16:25:34 -0700830 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700831 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700832 {
833 continue;
834 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800835
Jason M. Billse85d6b12019-07-29 17:01:15 -0700836 if (firstEntry)
837 {
838 firstEntry = false;
839 }
840
Jason M. Bills95820182019-04-22 16:25:34 -0700841 logEntryArray.push_back({});
842 nlohmann::json &bmcLogEntry = logEntryArray.back();
843 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
844 {
845 messages::internalError(asyncResp->res);
846 return;
847 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800848 }
849 }
850 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
851 if (skip + top < entryCount)
852 {
853 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700854 "/redfish/v1/Systems/system/LogServices/EventLog/"
855 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800856 std::to_string(skip + top);
857 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500858 }
859};
860
Jason M. Bills897967d2019-07-29 17:05:30 -0700861class JournalEventLogEntry : public Node
862{
863 public:
864 JournalEventLogEntry(CrowApp &app) :
865 Node(app,
866 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
867 std::string())
868 {
869 entityPrivileges = {
870 {boost::beast::http::verb::get, {{"Login"}}},
871 {boost::beast::http::verb::head, {{"Login"}}},
872 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
873 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
874 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
875 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
876 }
877
878 private:
879 void doGet(crow::Response &res, const crow::Request &req,
880 const std::vector<std::string> &params) override
881 {
882 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
883 if (params.size() != 1)
884 {
885 messages::internalError(asyncResp->res);
886 return;
887 }
888 const std::string &targetID = params[0];
889
890 // Go through the log files and check the unique ID for each entry to
891 // find the target entry
892 std::vector<std::filesystem::path> redfishLogFiles;
893 getRedfishLogFiles(redfishLogFiles);
894 std::string logEntry;
895
896 // Oldest logs are in the last file, so start there and loop backwards
897 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
898 it++)
899 {
900 std::ifstream logStream(*it);
901 if (!logStream.is_open())
902 {
903 continue;
904 }
905
906 // Reset the unique ID on the first entry
907 bool firstEntry = true;
908 while (std::getline(logStream, logEntry))
909 {
910 std::string idStr;
911 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
912 {
913 continue;
914 }
915
916 if (firstEntry)
917 {
918 firstEntry = false;
919 }
920
921 if (idStr == targetID)
922 {
923 if (fillEventLogEntryJson(idStr, logEntry,
924 asyncResp->res.jsonValue) != 0)
925 {
926 messages::internalError(asyncResp->res);
927 return;
928 }
929 return;
930 }
931 }
932 }
933 // Requested ID was not found
934 messages::resourceMissingAtURI(asyncResp->res, targetID);
935 }
936};
937
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500938class DBusEventLogEntryCollection : public Node
939{
940 public:
941 template <typename CrowApp>
942 DBusEventLogEntryCollection(CrowApp &app) :
943 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
944 {
945 entityPrivileges = {
946 {boost::beast::http::verb::get, {{"Login"}}},
947 {boost::beast::http::verb::head, {{"Login"}}},
948 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
949 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
950 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
951 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
952 }
953
954 private:
955 void doGet(crow::Response &res, const crow::Request &req,
956 const std::vector<std::string> &params) override
957 {
958 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
959
960 // Collections don't include the static data added by SubRoute because
961 // it has a duplicate entry for members
962 asyncResp->res.jsonValue["@odata.type"] =
963 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500964 asyncResp->res.jsonValue["@odata.id"] =
965 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
966 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
967 asyncResp->res.jsonValue["Description"] =
968 "Collection of System Event Log Entries";
969
Andrew Geisslercb92c032018-08-17 07:56:14 -0700970 // DBus implementation of EventLog/Entries
971 // Make call to Logging Service to find all log entry objects
972 crow::connections::systemBus->async_method_call(
973 [asyncResp](const boost::system::error_code ec,
974 GetManagedObjectsType &resp) {
975 if (ec)
976 {
977 // TODO Handle for specific error code
978 BMCWEB_LOG_ERROR
979 << "getLogEntriesIfaceData resp_handler got error "
980 << ec;
981 messages::internalError(asyncResp->res);
982 return;
983 }
984 nlohmann::json &entriesArray =
985 asyncResp->res.jsonValue["Members"];
986 entriesArray = nlohmann::json::array();
987 for (auto &objectPath : resp)
988 {
989 for (auto &interfaceMap : objectPath.second)
990 {
991 if (interfaceMap.first !=
992 "xyz.openbmc_project.Logging.Entry")
993 {
994 BMCWEB_LOG_DEBUG << "Bailing early on "
995 << interfaceMap.first;
996 continue;
997 }
998 entriesArray.push_back({});
999 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -07001000 uint32_t *id = nullptr;
1001 std::time_t timestamp{};
1002 std::string *severity = nullptr;
1003 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001004 for (auto &propertyMap : interfaceMap.second)
1005 {
1006 if (propertyMap.first == "Id")
1007 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001008 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001009 if (id == nullptr)
1010 {
1011 messages::propertyMissing(asyncResp->res,
1012 "Id");
1013 }
1014 }
1015 else if (propertyMap.first == "Timestamp")
1016 {
1017 const uint64_t *millisTimeStamp =
1018 std::get_if<uint64_t>(&propertyMap.second);
1019 if (millisTimeStamp == nullptr)
1020 {
1021 messages::propertyMissing(asyncResp->res,
1022 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001023 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001024 }
1025 // Retrieve Created property with format:
1026 // yyyy-mm-ddThh:mm:ss
1027 std::chrono::milliseconds chronoTimeStamp(
1028 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001029 timestamp = std::chrono::duration_cast<
1030 std::chrono::duration<int>>(
1031 chronoTimeStamp)
1032 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001033 }
1034 else if (propertyMap.first == "Severity")
1035 {
1036 severity = std::get_if<std::string>(
1037 &propertyMap.second);
1038 if (severity == nullptr)
1039 {
1040 messages::propertyMissing(asyncResp->res,
1041 "Severity");
1042 }
1043 }
1044 else if (propertyMap.first == "Message")
1045 {
1046 message = std::get_if<std::string>(
1047 &propertyMap.second);
1048 if (message == nullptr)
1049 {
1050 messages::propertyMissing(asyncResp->res,
1051 "Message");
1052 }
1053 }
1054 }
1055 thisEntry = {
1056 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001057 {"@odata.id",
1058 "/redfish/v1/Systems/system/LogServices/EventLog/"
1059 "Entries/" +
1060 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001061 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001062 {"Id", std::to_string(*id)},
1063 {"Message", *message},
1064 {"EntryType", "Event"},
1065 {"Severity",
1066 translateSeverityDbusToRedfish(*severity)},
1067 {"Created", crow::utility::getDateTime(timestamp)}};
1068 }
1069 }
1070 std::sort(entriesArray.begin(), entriesArray.end(),
1071 [](const nlohmann::json &left,
1072 const nlohmann::json &right) {
1073 return (left["Id"] <= right["Id"]);
1074 });
1075 asyncResp->res.jsonValue["Members@odata.count"] =
1076 entriesArray.size();
1077 },
1078 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1079 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001080 }
1081};
1082
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001083class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001084{
1085 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001086 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001087 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001088 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1089 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001090 {
1091 entityPrivileges = {
1092 {boost::beast::http::verb::get, {{"Login"}}},
1093 {boost::beast::http::verb::head, {{"Login"}}},
1094 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1095 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1096 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1097 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1098 }
1099
1100 private:
1101 void doGet(crow::Response &res, const crow::Request &req,
1102 const std::vector<std::string> &params) override
1103 {
1104 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001105 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001106 {
1107 messages::internalError(asyncResp->res);
1108 return;
1109 }
Ed Tanous029573d2019-02-01 10:57:49 -08001110 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001111
Andrew Geisslercb92c032018-08-17 07:56:14 -07001112 // DBus implementation of EventLog/Entries
1113 // Make call to Logging Service to find all log entry objects
1114 crow::connections::systemBus->async_method_call(
1115 [asyncResp, entryID](const boost::system::error_code ec,
1116 GetManagedPropertyType &resp) {
1117 if (ec)
1118 {
1119 BMCWEB_LOG_ERROR
1120 << "EventLogEntry (DBus) resp_handler got error " << ec;
1121 messages::internalError(asyncResp->res);
1122 return;
1123 }
Ed Tanous66664f22019-10-11 13:05:49 -07001124 uint32_t *id = nullptr;
1125 std::time_t timestamp{};
1126 std::string *severity = nullptr;
1127 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001128 for (auto &propertyMap : resp)
1129 {
1130 if (propertyMap.first == "Id")
1131 {
1132 id = std::get_if<uint32_t>(&propertyMap.second);
1133 if (id == nullptr)
1134 {
1135 messages::propertyMissing(asyncResp->res, "Id");
1136 }
1137 }
1138 else if (propertyMap.first == "Timestamp")
1139 {
1140 const uint64_t *millisTimeStamp =
1141 std::get_if<uint64_t>(&propertyMap.second);
1142 if (millisTimeStamp == nullptr)
1143 {
1144 messages::propertyMissing(asyncResp->res,
1145 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001146 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001147 }
1148 // Retrieve Created property with format:
1149 // yyyy-mm-ddThh:mm:ss
1150 std::chrono::milliseconds chronoTimeStamp(
1151 *millisTimeStamp);
1152 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001153 std::chrono::duration_cast<
1154 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001155 .count();
1156 }
1157 else if (propertyMap.first == "Severity")
1158 {
1159 severity =
1160 std::get_if<std::string>(&propertyMap.second);
1161 if (severity == nullptr)
1162 {
1163 messages::propertyMissing(asyncResp->res,
1164 "Severity");
1165 }
1166 }
1167 else if (propertyMap.first == "Message")
1168 {
1169 message = std::get_if<std::string>(&propertyMap.second);
1170 if (message == nullptr)
1171 {
1172 messages::propertyMissing(asyncResp->res,
1173 "Message");
1174 }
1175 }
1176 }
Ed Tanous271584a2019-07-09 16:24:22 -07001177 if (id == nullptr || message == nullptr || severity == nullptr)
1178 {
1179 return;
1180 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001181 asyncResp->res.jsonValue = {
1182 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001183 {"@odata.id",
1184 "/redfish/v1/Systems/system/LogServices/EventLog/"
1185 "Entries/" +
1186 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001187 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001188 {"Id", std::to_string(*id)},
1189 {"Message", *message},
1190 {"EntryType", "Event"},
1191 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001192 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001193 },
1194 "xyz.openbmc_project.Logging",
1195 "/xyz/openbmc_project/logging/entry/" + entryID,
1196 "org.freedesktop.DBus.Properties", "GetAll",
1197 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001198 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001199
1200 void doDelete(crow::Response &res, const crow::Request &req,
1201 const std::vector<std::string> &params) override
1202 {
1203
1204 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1205
1206 auto asyncResp = std::make_shared<AsyncResp>(res);
1207
1208 if (params.size() != 1)
1209 {
1210 messages::internalError(asyncResp->res);
1211 return;
1212 }
1213 std::string entryID = params[0];
1214
1215 dbus::utility::escapePathForDbus(entryID);
1216
1217 // Process response from Logging service.
1218 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1219 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1220 if (ec)
1221 {
1222 // TODO Handle for specific error code
1223 BMCWEB_LOG_ERROR
1224 << "EventLogEntry (DBus) doDelete respHandler got error "
1225 << ec;
1226 asyncResp->res.result(
1227 boost::beast::http::status::internal_server_error);
1228 return;
1229 }
1230
1231 asyncResp->res.result(boost::beast::http::status::ok);
1232 };
1233
1234 // Make call to Logging service to request Delete Log
1235 crow::connections::systemBus->async_method_call(
1236 respHandler, "xyz.openbmc_project.Logging",
1237 "/xyz/openbmc_project/logging/entry/" + entryID,
1238 "xyz.openbmc_project.Object.Delete", "Delete");
1239 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001240};
1241
1242class BMCLogServiceCollection : public Node
1243{
1244 public:
1245 template <typename CrowApp>
1246 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001247 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001248 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001249 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001250 {boost::beast::http::verb::get, {{"Login"}}},
1251 {boost::beast::http::verb::head, {{"Login"}}},
1252 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1253 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1254 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1255 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001256 }
1257
1258 private:
1259 /**
1260 * Functions triggers appropriate requests on DBus
1261 */
1262 void doGet(crow::Response &res, const crow::Request &req,
1263 const std::vector<std::string> &params) override
1264 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001265 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001266 // Collections don't include the static data added by SubRoute because
1267 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001268 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001269 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001270 asyncResp->res.jsonValue["@odata.id"] =
1271 "/redfish/v1/Managers/bmc/LogServices";
1272 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1273 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001274 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001275 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1276 logServiceArray = nlohmann::json::array();
1277#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1278 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001279 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001280#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001281 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001282 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001283 }
1284};
1285
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001286class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001287{
1288 public:
1289 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001290 BMCJournalLogService(CrowApp &app) :
1291 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001292 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001293 entityPrivileges = {
1294 {boost::beast::http::verb::get, {{"Login"}}},
1295 {boost::beast::http::verb::head, {{"Login"}}},
1296 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1297 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1298 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1299 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1300 }
1301
1302 private:
1303 void doGet(crow::Response &res, const crow::Request &req,
1304 const std::vector<std::string> &params) override
1305 {
1306 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001307 asyncResp->res.jsonValue["@odata.type"] =
1308 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001309 asyncResp->res.jsonValue["@odata.id"] =
1310 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001311 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1312 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1313 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001314 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001315 asyncResp->res.jsonValue["Entries"] = {
1316 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001317 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001318 }
1319};
1320
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001321static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1322 sd_journal *journal,
1323 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001324{
1325 // Get the Log Entry contents
1326 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001327
Ed Tanous39e77502019-03-04 17:35:53 -08001328 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001329 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001330 if (ret < 0)
1331 {
1332 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1333 return 1;
1334 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001335
1336 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001337 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001338 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001339 if (ret < 0)
1340 {
1341 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001342 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001343
1344 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001345 std::string entryTimeStr;
1346 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001347 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001348 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001349 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001350
1351 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001352 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001353 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001354 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1355 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001356 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001357 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001358 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001359 {"EntryType", "Oem"},
1360 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001361 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001362 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001363 {"Created", std::move(entryTimeStr)}};
1364 return 0;
1365}
1366
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001367class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001368{
1369 public:
1370 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001371 BMCJournalLogEntryCollection(CrowApp &app) :
1372 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001373 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001374 entityPrivileges = {
1375 {boost::beast::http::verb::get, {{"Login"}}},
1376 {boost::beast::http::verb::head, {{"Login"}}},
1377 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1378 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1379 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1380 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1381 }
1382
1383 private:
1384 void doGet(crow::Response &res, const crow::Request &req,
1385 const std::vector<std::string> &params) override
1386 {
1387 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001388 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001389 uint64_t skip = 0;
1390 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001391 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001392 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001393 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001394 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001395 if (!getTopParam(asyncResp->res, req, top))
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. Billse1f26342018-07-18 12:12:00 -07001399 // Collections don't include the static data added by SubRoute because
1400 // it has a duplicate entry for members
1401 asyncResp->res.jsonValue["@odata.type"] =
1402 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001403 asyncResp->res.jsonValue["@odata.id"] =
1404 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001405 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001406 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001407 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1408 asyncResp->res.jsonValue["Description"] =
1409 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001410 asyncResp->res.jsonValue["@odata.id"] =
1411 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001412 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1413 logEntryArray = nlohmann::json::array();
1414
1415 // Go through the journal and use the timestamp to create a unique ID
1416 // for each entry
1417 sd_journal *journalTmp = nullptr;
1418 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1419 if (ret < 0)
1420 {
1421 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001422 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001423 return;
1424 }
1425 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1426 journalTmp, sd_journal_close);
1427 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001428 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001429 // Reset the unique ID on the first entry
1430 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001431 SD_JOURNAL_FOREACH(journal.get())
1432 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001433 entryCount++;
1434 // Handle paging using skip (number of entries to skip from the
1435 // start) and top (number of entries to display)
1436 if (entryCount <= skip || entryCount > skip + top)
1437 {
1438 continue;
1439 }
1440
Jason M. Bills16428a12018-11-02 12:42:29 -07001441 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001442 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001443 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001444 continue;
1445 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001446
Jason M. Billse85d6b12019-07-29 17:01:15 -07001447 if (firstEntry)
1448 {
1449 firstEntry = false;
1450 }
1451
Jason M. Billse1f26342018-07-18 12:12:00 -07001452 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001453 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1454 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1455 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001456 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001457 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001458 return;
1459 }
1460 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001461 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1462 if (skip + top < entryCount)
1463 {
1464 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001465 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001466 std::to_string(skip + top);
1467 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001468 }
1469};
1470
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001471class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001472{
1473 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001474 BMCJournalLogEntry(CrowApp &app) :
1475 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001476 std::string())
1477 {
1478 entityPrivileges = {
1479 {boost::beast::http::verb::get, {{"Login"}}},
1480 {boost::beast::http::verb::head, {{"Login"}}},
1481 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1482 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1483 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1484 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1485 }
1486
1487 private:
1488 void doGet(crow::Response &res, const crow::Request &req,
1489 const std::vector<std::string> &params) override
1490 {
1491 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1492 if (params.size() != 1)
1493 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001494 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001495 return;
1496 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001497 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001498 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001499 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001500 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001501 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001502 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001503 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001504 }
1505
1506 sd_journal *journalTmp = nullptr;
1507 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1508 if (ret < 0)
1509 {
1510 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001511 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001512 return;
1513 }
1514 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1515 journalTmp, sd_journal_close);
1516 journalTmp = nullptr;
1517 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001518 // tracking the unique ID
1519 std::string idStr;
1520 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001521 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001522 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001523 {
1524 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001525 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1526 {
1527 messages::internalError(asyncResp->res);
1528 return;
1529 }
1530 if (firstEntry)
1531 {
1532 firstEntry = false;
1533 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001534 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001535 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001536 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001537 {
1538 messages::resourceMissingAtURI(asyncResp->res, entryID);
1539 return;
1540 }
1541
1542 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1543 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001544 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001545 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001546 return;
1547 }
1548 }
1549};
1550
raviteja-bc9bb6862020-02-03 11:53:32 -06001551class SystemDumpService : public Node
1552{
1553 public:
1554 template <typename CrowApp>
1555 SystemDumpService(CrowApp &app) :
1556 Node(app, "/redfish/v1/Systems/system/LogServices/System/")
1557 {
1558 entityPrivileges = {
1559 {boost::beast::http::verb::get, {{"Login"}}},
1560 {boost::beast::http::verb::head, {{"Login"}}},
1561 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1562 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1563 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1564 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1565 }
1566
1567 private:
1568 void doGet(crow::Response &res, const crow::Request &req,
1569 const std::vector<std::string> &params) override
1570 {
1571 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1572
1573 asyncResp->res.jsonValue["@odata.id"] =
1574 "/redfish/v1/Systems/system/LogServices/System";
1575 asyncResp->res.jsonValue["@odata.type"] =
1576 "#LogService.v1_1_0.LogService";
1577 asyncResp->res.jsonValue["Name"] = "Dump Log Service";
1578 asyncResp->res.jsonValue["Description"] = "System Dump Log Service";
1579 asyncResp->res.jsonValue["Id"] = "System";
1580 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1581 asyncResp->res.jsonValue["LogEntryTypes"] = "Dump";
1582 asyncResp->res.jsonValue["Oem"]["DumpType"] = "System";
1583
1584 asyncResp->res.jsonValue["Entries"] = {
1585 {"@odata.id",
1586 "/redfish/v1/Systems/system/LogServices/System/Entries"}};
1587 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1588 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1589 "Actions/LogService.ClearLog"}};
1590 asyncResp->res.jsonValue["Actions"]["#LogService.CreateLog"] = {
1591 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1592 "Actions/LogService.CreateLog"}};
1593 }
1594};
1595
1596class SystemDumpEntryCollection : public Node
1597{
1598 public:
1599 template <typename CrowApp>
1600 SystemDumpEntryCollection(CrowApp &app) :
1601 Node(app, "/redfish/v1/Systems/system/LogServices/System/Entries/")
1602 {
1603 entityPrivileges = {
1604 {boost::beast::http::verb::get, {{"Login"}}},
1605 {boost::beast::http::verb::head, {{"Login"}}},
1606 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1607 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1608 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1609 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1610 }
1611
1612 private:
1613 /**
1614 * Functions triggers appropriate requests on DBus
1615 */
1616 void doGet(crow::Response &res, const crow::Request &req,
1617 const std::vector<std::string> &params) override
1618 {
1619 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1620
1621 asyncResp->res.jsonValue["@odata.type"] =
1622 "#LogEntryCollection.LogEntryCollection";
1623 asyncResp->res.jsonValue["@odata.id"] =
1624 "/redfish/v1/Systems/system/LogServices/System/Entries";
1625 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
1626 asyncResp->res.jsonValue["Description"] =
1627 "Collection of System Dump Entries";
1628
1629 crow::connections::systemBus->async_method_call(
1630 [asyncResp](const boost::system::error_code ec,
1631 const crow::openbmc_mapper::GetSubTreeType &resp) {
1632 if (ec)
1633 {
1634 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1635 messages::internalError(asyncResp->res);
1636 return;
1637 }
1638
1639 nlohmann::json &logArray = asyncResp->res.jsonValue["Members"];
1640 logArray = nlohmann::json::array();
1641 for (auto &object : resp)
1642 {
1643 const std::string &path =
1644 static_cast<const std::string &>(object.first);
1645 std::size_t lastPos = path.rfind("/");
1646 if (lastPos == std::string::npos)
1647 {
1648 continue;
1649 }
1650 std::string logID = path.substr(lastPos + 1);
1651 logArray.push_back(
1652 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1653 "System/Entries/" +
1654 logID}});
1655 }
1656 asyncResp->res.jsonValue["Members@odata.count"] =
1657 logArray.size();
1658 },
1659 "xyz.openbmc_project.ObjectMapper",
1660 "/xyz/openbmc_project/object_mapper",
1661 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1662 "/xyz/openbmc_project/dump", 0,
1663 std::array<const char *, 1>{
1664 "xyz.openbmc_project.Dump.Entry.System"});
1665 }
1666};
1667
1668class SystemDumpEntry : public Node
1669{
1670 public:
1671 SystemDumpEntry(CrowApp &app) :
1672 Node(app,
1673 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/",
1674 std::string())
1675 {
1676 entityPrivileges = {
1677 {boost::beast::http::verb::get, {{"Login"}}},
1678 {boost::beast::http::verb::head, {{"Login"}}},
1679 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1680 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1681 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1682 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1683 }
1684
1685 private:
1686 void doGet(crow::Response &res, const crow::Request &req,
1687 const std::vector<std::string> &params) override
1688 {
1689 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1690 if (params.size() != 1)
1691 {
1692 messages::internalError(asyncResp->res);
1693 return;
1694 }
1695 const std::string &entryID = params[0];
1696 crow::connections::systemBus->async_method_call(
1697 [asyncResp, entryID](const boost::system::error_code ec,
1698 GetManagedObjectsType &resp) {
1699 if (ec)
1700 {
1701 BMCWEB_LOG_ERROR
1702 << "SystemDumpEntry resp_handler got error " << ec;
1703 messages::internalError(asyncResp->res);
1704 return;
1705 }
1706
1707 for (auto &objectPath : resp)
1708 {
1709 if (objectPath.first.str.find(
1710 "/xyz/openbmc_project/dump/entry/" + entryID) ==
1711 std::string::npos)
1712 {
1713 continue;
1714 }
1715
1716 bool foundSystemDumpEntry = false;
1717 for (auto &interfaceMap : objectPath.second)
1718 {
1719 if (interfaceMap.first ==
1720 "xyz.openbmc_project.Dump.Entry.System")
1721 {
1722 foundSystemDumpEntry = true;
1723 break;
1724 }
1725 }
1726 if (foundSystemDumpEntry == false)
1727 {
1728 BMCWEB_LOG_DEBUG << "Can't find System Dump Entry";
1729 messages::internalError(asyncResp->res);
1730 return;
1731 }
1732
1733 std::string timestamp{};
1734 uint64_t size = 0;
1735
1736 for (auto &interfaceMap : objectPath.second)
1737 {
1738 if (interfaceMap.first ==
1739 "xyz.openbmc_project.Dump.Entry")
1740 {
1741 for (auto &propertyMap : interfaceMap.second)
1742 {
1743 if (propertyMap.first == "Size")
1744 {
1745 auto sizePtr = std::get_if<uint64_t>(
1746 &propertyMap.second);
1747 if (sizePtr == nullptr)
1748 {
1749 messages::propertyMissing(
1750 asyncResp->res, "Size");
1751 break;
1752 }
1753 size = *sizePtr;
1754 break;
1755 }
1756 }
1757 }
1758 else if (interfaceMap.first ==
1759 "xyz.openbmc_project.Time.EpochTime")
1760 {
1761 for (auto &propertyMap : interfaceMap.second)
1762 {
1763 if (propertyMap.first == "Elapsed")
1764 {
1765 const uint64_t *usecsTimeStamp =
1766 std::get_if<uint64_t>(
1767 &propertyMap.second);
1768 if (usecsTimeStamp == nullptr)
1769 {
1770 messages::propertyMissing(
1771 asyncResp->res, "Elapsed");
1772 break;
1773 }
1774 getTimestampStr(*usecsTimeStamp, timestamp);
1775 break;
1776 }
1777 }
1778 }
1779 }
1780 asyncResp->res.jsonValue = {
1781 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1782 {"@odata.id",
1783 "/redfish/v1/Systems/system/LogServices/System/"
1784 "Entries/" +
1785 entryID},
1786 {"Name", "System Dump Entry"},
1787 {"Id", entryID},
1788 {"SizeInB", size},
1789 {"EntryType", "Dump"},
1790 {"EntryCode", "User generated dump"},
1791 {"Created", timestamp}};
1792
1793 asyncResp->res
1794 .jsonValue["Actions"]["#LogEntry.DownloadLog"] = {
1795 {"target",
1796 "/redfish/v1/Systems/system/LogServices/System/"
1797 "Entries/" +
1798 entryID + "/Actions/LogEntry.DownloadLog"}};
1799 }
1800 },
1801 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
1802 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1803 }
1804
1805 void doDelete(crow::Response &res, const crow::Request &req,
1806 const std::vector<std::string> &params) override
1807 {
1808 BMCWEB_LOG_DEBUG << "Do delete single dump entry";
1809
1810 auto asyncResp = std::make_shared<AsyncResp>(res);
1811
1812 if (params.size() != 1)
1813 {
1814 messages::internalError(asyncResp->res);
1815 return;
1816 }
1817 std::string entryID = params[0];
1818
1819 crow::connections::systemBus->async_method_call(
1820 [asyncResp,
1821 entryID](const boost::system::error_code ec,
1822 const crow::openbmc_mapper::GetSubTreeType &resp) {
1823 if (ec)
1824 {
1825 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1826 messages::internalError(asyncResp->res);
1827 return;
1828 }
1829
1830 for (auto &object : resp)
1831 {
1832 const std::string &path =
1833 static_cast<const std::string &>(object.first);
1834
1835 std::size_t pos = path.rfind(
1836 "/xyz/openbmc_project/dump/entry/" + entryID);
1837 if (pos != std::string::npos)
1838 {
1839 deleteSystemDumpEntry(asyncResp->res, entryID);
1840 return;
1841 }
1842 }
1843 },
1844 "xyz.openbmc_project.ObjectMapper",
1845 "/xyz/openbmc_project/object_mapper",
1846 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1847 "/xyz/openbmc_project/dump", 0,
1848 std::array<const char *, 1>{
1849 "xyz.openbmc_project.Dump.Entry.System"});
1850 }
1851};
1852
raviteja-b06578432020-02-03 12:50:42 -06001853class SystemDumpEntryDownload : public Node
1854{
1855 public:
1856 SystemDumpEntryDownload(CrowApp &app) :
1857 Node(app,
1858 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/"
1859 "Actions/"
1860 "LogEntry.DownloadLog/",
1861 std::string())
1862 {
1863 entityPrivileges = {
1864 {boost::beast::http::verb::get, {{"Login"}}},
1865 {boost::beast::http::verb::head, {{"Login"}}},
1866 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1867 }
1868
1869 private:
1870 void doPost(crow::Response &res, const crow::Request &req,
1871 const std::vector<std::string> &params) override
1872 {
1873 if (params.size() != 1)
1874 {
1875 messages::internalError(res);
1876 return;
1877 }
1878 const std::string &entryID = params[0];
1879 crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID);
1880 }
1881};
1882
Jason M. Bills424c4172019-03-21 13:50:33 -07001883class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001884{
1885 public:
1886 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001887 CrashdumpService(CrowApp &app) :
1888 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001889 {
AppaRao Puli39460282020-04-07 17:03:04 +05301890 // Note: Deviated from redfish privilege registry for GET & HEAD
1891 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001892 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301893 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1894 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001895 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1896 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1897 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1898 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001899 }
1900
1901 private:
1902 /**
1903 * Functions triggers appropriate requests on DBus
1904 */
1905 void doGet(crow::Response &res, const crow::Request &req,
1906 const std::vector<std::string> &params) override
1907 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001908 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001909 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001910 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001911 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001912 asyncResp->res.jsonValue["@odata.type"] =
1913 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001914 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1915 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1916 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001917 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1918 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001919 asyncResp->res.jsonValue["Entries"] = {
1920 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001921 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001922 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001923 {"#LogService.ClearLog",
1924 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1925 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001926 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001927 {{"#Crashdump.OnDemand",
1928 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1929 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001930
1931#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001932 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001933 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001934 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1935 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001936#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001937 }
1938};
1939
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001940class CrashdumpClear : public Node
1941{
1942 public:
1943 CrashdumpClear(CrowApp &app) :
1944 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1945 "LogService.ClearLog/")
1946 {
AppaRao Puli39460282020-04-07 17:03:04 +05301947 // Note: Deviated from redfish privilege registry for GET & HEAD
1948 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001949 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301950 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1951 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001952 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1953 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1954 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1955 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1956 }
1957
1958 private:
1959 void doPost(crow::Response &res, const crow::Request &req,
1960 const std::vector<std::string> &params) override
1961 {
1962 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1963
1964 crow::connections::systemBus->async_method_call(
1965 [asyncResp](const boost::system::error_code ec,
1966 const std::string &resp) {
1967 if (ec)
1968 {
1969 messages::internalError(asyncResp->res);
1970 return;
1971 }
1972 messages::success(asyncResp->res);
1973 },
1974 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
1975 }
1976};
1977
Jason M. Billse855dd22019-10-08 11:37:48 -07001978static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1979 const std::string &logID,
1980 nlohmann::json &logEntryJson)
1981{
Johnathan Mantey043a0532020-03-10 17:15:28 -07001982 auto getStoredLogCallback =
1983 [asyncResp, logID, &logEntryJson](
1984 const boost::system::error_code ec,
1985 const std::vector<std::pair<std::string, VariantType>> &params) {
1986 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001987 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07001988 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1989 if (ec.value() ==
1990 boost::system::linux_error::bad_request_descriptor)
1991 {
1992 messages::resourceNotFound(asyncResp->res, "LogEntry",
1993 logID);
1994 }
1995 else
1996 {
1997 messages::internalError(asyncResp->res);
1998 }
1999 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002000 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002001
Johnathan Mantey043a0532020-03-10 17:15:28 -07002002 std::string timestamp{};
2003 std::string filename{};
2004 std::string logfile{};
2005 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2006
2007 if (filename.empty() || timestamp.empty())
2008 {
2009 messages::resourceMissingAtURI(asyncResp->res, logID);
2010 return;
2011 }
2012
2013 std::string crashdumpURI =
2014 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2015 logID + "/" + filename;
2016 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2017 {"@odata.id", "/redfish/v1/Systems/system/"
2018 "LogServices/Crashdump/Entries/" +
2019 logID},
2020 {"Name", "CPU Crashdump"},
2021 {"Id", logID},
2022 {"EntryType", "Oem"},
2023 {"OemRecordFormat", "Crashdump URI"},
2024 {"Message", std::move(crashdumpURI)},
2025 {"Created", std::move(timestamp)}};
2026 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002027 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002028 std::move(getStoredLogCallback), crashdumpObject,
2029 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002030 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002031}
2032
Jason M. Bills424c4172019-03-21 13:50:33 -07002033class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002034{
2035 public:
2036 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07002037 CrashdumpEntryCollection(CrowApp &app) :
2038 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002039 {
AppaRao Puli39460282020-04-07 17:03:04 +05302040 // Note: Deviated from redfish privilege registry for GET & HEAD
2041 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002042 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302043 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2044 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002045 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2046 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2047 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2048 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002049 }
2050
2051 private:
2052 /**
2053 * Functions triggers appropriate requests on DBus
2054 */
2055 void doGet(crow::Response &res, const crow::Request &req,
2056 const std::vector<std::string> &params) override
2057 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002058 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002059 // Collections don't include the static data added by SubRoute because
2060 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002061 auto getLogEntriesCallback = [asyncResp](
2062 const boost::system::error_code ec,
2063 const std::vector<std::string> &resp) {
2064 if (ec)
2065 {
2066 if (ec.value() !=
2067 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002068 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002069 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2070 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002071 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002072 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002073 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002074 }
2075 asyncResp->res.jsonValue["@odata.type"] =
2076 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002077 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002078 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002079 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002080 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002081 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002082 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
2083 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002084 std::vector<std::string> logIDs;
2085 // Get the list of log entries and build up an empty array big
2086 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07002087 for (const std::string &objpath : resp)
2088 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002089 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002090 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002091 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002092 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002093 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002094 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002095 logIDs.emplace_back(objpath.substr(lastPos + 1));
2096
2097 // Add a space for the log entry to the array
2098 logEntryArray.push_back({});
2099 }
2100 // Now go through and set up async calls to fill in the entries
2101 size_t index = 0;
2102 for (const std::string &logID : logIDs)
2103 {
2104 // Add the log entry to the array
2105 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002106 }
2107 asyncResp->res.jsonValue["Members@odata.count"] =
2108 logEntryArray.size();
2109 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002110 crow::connections::systemBus->async_method_call(
2111 std::move(getLogEntriesCallback),
2112 "xyz.openbmc_project.ObjectMapper",
2113 "/xyz/openbmc_project/object_mapper",
2114 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002115 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002116 }
2117};
2118
Jason M. Bills424c4172019-03-21 13:50:33 -07002119class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002120{
2121 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002122 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002123 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002124 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002125 std::string())
2126 {
AppaRao Puli39460282020-04-07 17:03:04 +05302127 // Note: Deviated from redfish privilege registry for GET & HEAD
2128 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002129 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302130 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2131 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002132 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2133 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2134 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2135 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002136 }
2137
2138 private:
2139 void doGet(crow::Response &res, const crow::Request &req,
2140 const std::vector<std::string> &params) override
2141 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002142 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002143 if (params.size() != 1)
2144 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002145 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002146 return;
2147 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002148 const std::string &logID = params[0];
2149 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2150 }
2151};
2152
2153class CrashdumpFile : public Node
2154{
2155 public:
2156 CrashdumpFile(CrowApp &app) :
2157 Node(app,
2158 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2159 "<str>/",
2160 std::string(), std::string())
2161 {
AppaRao Puli39460282020-04-07 17:03:04 +05302162 // Note: Deviated from redfish privilege registry for GET & HEAD
2163 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002164 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302165 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2166 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002167 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2168 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2169 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2170 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2171 }
2172
2173 private:
2174 void doGet(crow::Response &res, const crow::Request &req,
2175 const std::vector<std::string> &params) override
2176 {
2177 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2178 if (params.size() != 2)
2179 {
2180 messages::internalError(asyncResp->res);
2181 return;
2182 }
2183 const std::string &logID = params[0];
2184 const std::string &fileName = params[1];
2185
Johnathan Mantey043a0532020-03-10 17:15:28 -07002186 auto getStoredLogCallback =
2187 [asyncResp, logID, fileName](
2188 const boost::system::error_code ec,
2189 const std::vector<std::pair<std::string, VariantType>> &resp) {
2190 if (ec)
2191 {
2192 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2193 << ec.message();
2194 messages::internalError(asyncResp->res);
2195 return;
2196 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002197
Johnathan Mantey043a0532020-03-10 17:15:28 -07002198 std::string dbusFilename{};
2199 std::string dbusTimestamp{};
2200 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002201
Johnathan Mantey043a0532020-03-10 17:15:28 -07002202 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2203 dbusFilepath);
2204
2205 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2206 dbusFilepath.empty())
2207 {
2208 messages::resourceMissingAtURI(asyncResp->res, fileName);
2209 return;
2210 }
2211
2212 // Verify the file name parameter is correct
2213 if (fileName != dbusFilename)
2214 {
2215 messages::resourceMissingAtURI(asyncResp->res, fileName);
2216 return;
2217 }
2218
2219 if (!std::filesystem::exists(dbusFilepath))
2220 {
2221 messages::resourceMissingAtURI(asyncResp->res, fileName);
2222 return;
2223 }
2224 std::ifstream ifs(dbusFilepath, std::ios::in |
2225 std::ios::binary |
2226 std::ios::ate);
2227 std::ifstream::pos_type fileSize = ifs.tellg();
2228 if (fileSize < 0)
2229 {
2230 messages::generalError(asyncResp->res);
2231 return;
2232 }
2233 ifs.seekg(0, std::ios::beg);
2234
2235 auto crashData = std::make_unique<char[]>(
2236 static_cast<unsigned int>(fileSize));
2237
2238 ifs.read(crashData.get(), static_cast<int>(fileSize));
2239
2240 // The cast to std::string is intentional in order to use the
2241 // assign() that applies move mechanics
2242 asyncResp->res.body().assign(
2243 static_cast<std::string>(crashData.get()));
2244
2245 // Configure this to be a file download when accessed from
2246 // a browser
2247 asyncResp->res.addHeader("Content-Disposition", "attachment");
2248 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002249 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002250 std::move(getStoredLogCallback), crashdumpObject,
2251 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002252 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002253 }
2254};
2255
Jason M. Bills424c4172019-03-21 13:50:33 -07002256class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002257{
2258 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002259 OnDemandCrashdump(CrowApp &app) :
2260 Node(app,
2261 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2262 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002263 {
AppaRao Puli39460282020-04-07 17:03:04 +05302264 // Note: Deviated from redfish privilege registry for GET & HEAD
2265 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002266 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302267 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2268 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2269 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2270 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2271 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2272 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002273 }
2274
2275 private:
2276 void doPost(crow::Response &res, const crow::Request &req,
2277 const std::vector<std::string> &params) override
2278 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002279 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002280
James Feistfe306722020-03-12 16:32:08 -07002281 auto generateonDemandLogCallback = [asyncResp,
2282 req](const boost::system::error_code
2283 ec,
2284 const std::string &resp) {
James Feist46229572020-02-19 15:11:58 -08002285 if (ec)
2286 {
2287 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002288 {
James Feist46229572020-02-19 15:11:58 -08002289 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002290 }
James Feist46229572020-02-19 15:11:58 -08002291 else if (ec.value() ==
2292 boost::system::errc::device_or_resource_busy)
2293 {
2294 messages::serviceTemporarilyUnavailable(asyncResp->res,
2295 "60");
2296 }
2297 else
2298 {
2299 messages::internalError(asyncResp->res);
2300 }
2301 return;
2302 }
2303 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08002304 [](boost::system::error_code err, sdbusplus::message::message &,
2305 const std::shared_ptr<task::TaskData> &taskData) {
2306 if (!err)
2307 {
James Feiste5d50062020-05-11 17:29:00 -07002308 taskData->messages.emplace_back(
2309 messages::taskCompletedOK(
2310 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002311 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002312 }
James Feist32898ce2020-03-10 16:16:52 -07002313 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002314 },
James Feist46229572020-02-19 15:11:58 -08002315 "type='signal',interface='org.freedesktop.DBus.Properties',"
2316 "member='PropertiesChanged',arg0namespace='com.intel."
2317 "crashdump'");
2318 task->startTimer(std::chrono::minutes(5));
2319 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002320 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002321 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002322 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002323 std::move(generateonDemandLogCallback), crashdumpObject,
2324 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002325 }
2326};
2327
Jason M. Billse1f26342018-07-18 12:12:00 -07002328class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002329{
2330 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07002331 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002332 Node(app,
2333 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2334 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002335 {
AppaRao Puli39460282020-04-07 17:03:04 +05302336 // Note: Deviated from redfish privilege registry for GET & HEAD
2337 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002338 entityPrivileges = {
2339 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2340 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2341 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2342 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2343 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2344 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2345 }
2346
2347 private:
2348 void doPost(crow::Response &res, const crow::Request &req,
2349 const std::vector<std::string> &params) override
2350 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002351 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002352 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002353
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002354 nlohmann::json reqJson =
2355 nlohmann::json::parse(req.body, nullptr, false);
2356 if (reqJson.find("PECICommands") != reqJson.end())
2357 {
2358 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2359 {
2360 return;
2361 }
2362 uint32_t idx = 0;
2363 for (auto const &cmd : peciCommands)
2364 {
2365 if (cmd.size() < 3)
2366 {
2367 std::string s("[");
2368 for (auto const &val : cmd)
2369 {
2370 if (val != *cmd.begin())
2371 {
2372 s += ",";
2373 }
2374 s += std::to_string(val);
2375 }
2376 s += "]";
2377 messages::actionParameterValueFormatError(
2378 res, s, "PECICommands[" + std::to_string(idx) + "]",
2379 "SendRawPeci");
2380 return;
2381 }
2382 idx++;
2383 }
2384 }
2385 else
2386 {
2387 /* This interface is deprecated */
2388 uint8_t clientAddress = 0;
2389 uint8_t readLength = 0;
2390 std::vector<uint8_t> peciCommand;
2391 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2392 "ReadLength", readLength, "PECICommand",
2393 peciCommand))
2394 {
2395 return;
2396 }
2397 peciCommands.push_back({clientAddress, 0, readLength});
2398 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2399 peciCommand.end());
2400 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002401 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002402 auto sendRawPECICallback =
2403 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002404 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002405 if (ec)
2406 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002407 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002408 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002409 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002410 return;
2411 }
2412 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2413 {"PECIResponse", resp}};
2414 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002415 // Call the SendRawPECI command with the provided data
2416 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002417 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002418 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002419 }
2420};
2421
Andrew Geisslercb92c032018-08-17 07:56:14 -07002422/**
2423 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2424 */
2425class DBusLogServiceActionsClear : public Node
2426{
2427 public:
2428 DBusLogServiceActionsClear(CrowApp &app) :
2429 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002430 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002431 {
2432 entityPrivileges = {
2433 {boost::beast::http::verb::get, {{"Login"}}},
2434 {boost::beast::http::verb::head, {{"Login"}}},
2435 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2436 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2437 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2438 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2439 }
2440
2441 private:
2442 /**
2443 * Function handles POST method request.
2444 * The Clear Log actions does not require any parameter.The action deletes
2445 * all entries found in the Entries collection for this Log Service.
2446 */
2447 void doPost(crow::Response &res, const crow::Request &req,
2448 const std::vector<std::string> &params) override
2449 {
2450 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2451
2452 auto asyncResp = std::make_shared<AsyncResp>(res);
2453 // Process response from Logging service.
2454 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2455 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2456 if (ec)
2457 {
2458 // TODO Handle for specific error code
2459 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2460 asyncResp->res.result(
2461 boost::beast::http::status::internal_server_error);
2462 return;
2463 }
2464
2465 asyncResp->res.result(boost::beast::http::status::no_content);
2466 };
2467
2468 // Make call to Logging service to request Clear Log
2469 crow::connections::systemBus->async_method_call(
2470 resp_handler, "xyz.openbmc_project.Logging",
2471 "/xyz/openbmc_project/logging",
2472 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2473 }
2474};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002475
2476/****************************************************
2477 * Redfish PostCode interfaces
2478 * using DBUS interface: getPostCodesTS
2479 ******************************************************/
2480class PostCodesLogService : public Node
2481{
2482 public:
2483 PostCodesLogService(CrowApp &app) :
2484 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2485 {
2486 entityPrivileges = {
2487 {boost::beast::http::verb::get, {{"Login"}}},
2488 {boost::beast::http::verb::head, {{"Login"}}},
2489 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2490 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2491 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2492 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2493 }
2494
2495 private:
2496 void doGet(crow::Response &res, const crow::Request &req,
2497 const std::vector<std::string> &params) override
2498 {
2499 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2500
2501 asyncResp->res.jsonValue = {
2502 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2503 {"@odata.type", "#LogService.v1_1_0.LogService"},
2504 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2505 {"Name", "POST Code Log Service"},
2506 {"Description", "POST Code Log Service"},
2507 {"Id", "BIOS POST Code Log"},
2508 {"OverWritePolicy", "WrapsWhenFull"},
2509 {"Entries",
2510 {{"@odata.id",
2511 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2512 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2513 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2514 "Actions/LogService.ClearLog"}};
2515 }
2516};
2517
2518class PostCodesClear : public Node
2519{
2520 public:
2521 PostCodesClear(CrowApp &app) :
2522 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2523 "LogService.ClearLog/")
2524 {
2525 entityPrivileges = {
2526 {boost::beast::http::verb::get, {{"Login"}}},
2527 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302528 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2529 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2530 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2531 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002532 }
2533
2534 private:
2535 void doPost(crow::Response &res, const crow::Request &req,
2536 const std::vector<std::string> &params) override
2537 {
2538 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2539
2540 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2541 // Make call to post-code service to request clear all
2542 crow::connections::systemBus->async_method_call(
2543 [asyncResp](const boost::system::error_code ec) {
2544 if (ec)
2545 {
2546 // TODO Handle for specific error code
2547 BMCWEB_LOG_ERROR
2548 << "doClearPostCodes resp_handler got error " << ec;
2549 asyncResp->res.result(
2550 boost::beast::http::status::internal_server_error);
2551 messages::internalError(asyncResp->res);
2552 return;
2553 }
2554 },
2555 "xyz.openbmc_project.State.Boot.PostCode",
2556 "/xyz/openbmc_project/State/Boot/PostCode",
2557 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2558 }
2559};
2560
2561static void fillPostCodeEntry(
2562 std::shared_ptr<AsyncResp> aResp,
2563 const boost::container::flat_map<uint64_t, uint64_t> &postcode,
2564 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2565 const uint64_t skip = 0, const uint64_t top = 0)
2566{
2567 // Get the Message from the MessageRegistry
2568 const message_registries::Message *message =
2569 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
2570 std::string severity;
2571 if (message != nullptr)
2572 {
2573 severity = message->severity;
2574 }
2575
2576 uint64_t currentCodeIndex = 0;
2577 nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"];
2578
2579 uint64_t firstCodeTimeUs = 0;
2580 for (const std::pair<uint64_t, uint64_t> &code : postcode)
2581 {
2582 currentCodeIndex++;
2583 std::string postcodeEntryID =
2584 "B" + std::to_string(bootIndex) + "-" +
2585 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2586
2587 uint64_t usecSinceEpoch = code.first;
2588 uint64_t usTimeOffset = 0;
2589
2590 if (1 == currentCodeIndex)
2591 { // already incremented
2592 firstCodeTimeUs = code.first;
2593 }
2594 else
2595 {
2596 usTimeOffset = code.first - firstCodeTimeUs;
2597 }
2598
2599 // skip if no specific codeIndex is specified and currentCodeIndex does
2600 // not fall between top and skip
2601 if ((codeIndex == 0) &&
2602 (currentCodeIndex <= skip || currentCodeIndex > top))
2603 {
2604 continue;
2605 }
2606
2607 // skip if a sepcific codeIndex is specified and does not match the
2608 // currentIndex
2609 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2610 {
2611 // This is done for simplicity. 1st entry is needed to calculate
2612 // time offset. To improve efficiency, one can get to the entry
2613 // directly (possibly with flatmap's nth method)
2614 continue;
2615 }
2616
2617 // currentCodeIndex is within top and skip or equal to specified code
2618 // index
2619
2620 // Get the Created time from the timestamp
2621 std::string entryTimeStr;
2622 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2623 {
2624 continue;
2625 }
2626
2627 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2628 std::ostringstream hexCode;
2629 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2630 << code.second;
2631 std::ostringstream timeOffsetStr;
2632 // Set Fixed -Point Notation
2633 timeOffsetStr << std::fixed;
2634 // Set precision to 4 digits
2635 timeOffsetStr << std::setprecision(4);
2636 // Add double to stream
2637 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2638 std::vector<std::string> messageArgs = {
2639 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2640
2641 // Get MessageArgs template from message registry
2642 std::string msg;
2643 if (message != nullptr)
2644 {
2645 msg = message->message;
2646
2647 // fill in this post code value
2648 int i = 0;
2649 for (const std::string &messageArg : messageArgs)
2650 {
2651 std::string argStr = "%" + std::to_string(++i);
2652 size_t argPos = msg.find(argStr);
2653 if (argPos != std::string::npos)
2654 {
2655 msg.replace(argPos, argStr.length(), messageArg);
2656 }
2657 }
2658 }
2659
2660 // add to AsyncResp
2661 logEntryArray.push_back({});
2662 nlohmann::json &bmcLogEntry = logEntryArray.back();
2663 bmcLogEntry = {
2664 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2665 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2666 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2667 "PostCodes/Entries/" +
2668 postcodeEntryID},
2669 {"Name", "POST Code Log Entry"},
2670 {"Id", postcodeEntryID},
2671 {"Message", std::move(msg)},
2672 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2673 {"MessageArgs", std::move(messageArgs)},
2674 {"EntryType", "Event"},
2675 {"Severity", std::move(severity)},
2676 {"Created", std::move(entryTimeStr)}};
2677 }
2678}
2679
2680static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2681 const uint16_t bootIndex,
2682 const uint64_t codeIndex)
2683{
2684 crow::connections::systemBus->async_method_call(
2685 [aResp, bootIndex, codeIndex](
2686 const boost::system::error_code ec,
2687 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2688 if (ec)
2689 {
2690 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2691 messages::internalError(aResp->res);
2692 return;
2693 }
2694
2695 // skip the empty postcode boots
2696 if (postcode.empty())
2697 {
2698 return;
2699 }
2700
2701 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2702
2703 aResp->res.jsonValue["Members@odata.count"] =
2704 aResp->res.jsonValue["Members"].size();
2705 },
2706 "xyz.openbmc_project.State.Boot.PostCode",
2707 "/xyz/openbmc_project/State/Boot/PostCode",
2708 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2709 bootIndex);
2710}
2711
2712static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2713 const uint16_t bootIndex,
2714 const uint16_t bootCount,
2715 const uint64_t entryCount, const uint64_t skip,
2716 const uint64_t top)
2717{
2718 crow::connections::systemBus->async_method_call(
2719 [aResp, bootIndex, bootCount, entryCount, skip,
2720 top](const boost::system::error_code ec,
2721 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2722 if (ec)
2723 {
2724 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2725 messages::internalError(aResp->res);
2726 return;
2727 }
2728
2729 uint64_t endCount = entryCount;
2730 if (!postcode.empty())
2731 {
2732 endCount = entryCount + postcode.size();
2733
2734 if ((skip < endCount) && ((top + skip) > entryCount))
2735 {
2736 uint64_t thisBootSkip =
2737 std::max(skip, entryCount) - entryCount;
2738 uint64_t thisBootTop =
2739 std::min(top + skip, endCount) - entryCount;
2740
2741 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2742 thisBootSkip, thisBootTop);
2743 }
2744 aResp->res.jsonValue["Members@odata.count"] = endCount;
2745 }
2746
2747 // continue to previous bootIndex
2748 if (bootIndex < bootCount)
2749 {
2750 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2751 bootCount, endCount, skip, top);
2752 }
2753 else
2754 {
2755 aResp->res.jsonValue["Members@odata.nextLink"] =
2756 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2757 "Entries?$skip=" +
2758 std::to_string(skip + top);
2759 }
2760 },
2761 "xyz.openbmc_project.State.Boot.PostCode",
2762 "/xyz/openbmc_project/State/Boot/PostCode",
2763 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2764 bootIndex);
2765}
2766
2767static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2768 const uint64_t skip, const uint64_t top)
2769{
2770 uint64_t entryCount = 0;
2771 crow::connections::systemBus->async_method_call(
2772 [aResp, entryCount, skip,
2773 top](const boost::system::error_code ec,
2774 const std::variant<uint16_t> &bootCount) {
2775 if (ec)
2776 {
2777 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2778 messages::internalError(aResp->res);
2779 return;
2780 }
2781 auto pVal = std::get_if<uint16_t>(&bootCount);
2782 if (pVal)
2783 {
2784 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2785 }
2786 else
2787 {
2788 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2789 }
2790 },
2791 "xyz.openbmc_project.State.Boot.PostCode",
2792 "/xyz/openbmc_project/State/Boot/PostCode",
2793 "org.freedesktop.DBus.Properties", "Get",
2794 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2795}
2796
2797class PostCodesEntryCollection : public Node
2798{
2799 public:
2800 template <typename CrowApp>
2801 PostCodesEntryCollection(CrowApp &app) :
2802 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2803 {
2804 entityPrivileges = {
2805 {boost::beast::http::verb::get, {{"Login"}}},
2806 {boost::beast::http::verb::head, {{"Login"}}},
2807 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2808 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2809 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2810 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2811 }
2812
2813 private:
2814 void doGet(crow::Response &res, const crow::Request &req,
2815 const std::vector<std::string> &params) override
2816 {
2817 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2818
2819 asyncResp->res.jsonValue["@odata.type"] =
2820 "#LogEntryCollection.LogEntryCollection";
2821 asyncResp->res.jsonValue["@odata.context"] =
2822 "/redfish/v1/"
2823 "$metadata#LogEntryCollection.LogEntryCollection";
2824 asyncResp->res.jsonValue["@odata.id"] =
2825 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2826 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2827 asyncResp->res.jsonValue["Description"] =
2828 "Collection of POST Code Log Entries";
2829 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2830 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2831
2832 uint64_t skip = 0;
2833 uint64_t top = maxEntriesPerPage; // Show max entries by default
2834 if (!getSkipParam(asyncResp->res, req, skip))
2835 {
2836 return;
2837 }
2838 if (!getTopParam(asyncResp->res, req, top))
2839 {
2840 return;
2841 }
2842 getCurrentBootNumber(asyncResp, skip, top);
2843 }
2844};
2845
2846class PostCodesEntry : public Node
2847{
2848 public:
2849 PostCodesEntry(CrowApp &app) :
2850 Node(app,
2851 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2852 std::string())
2853 {
2854 entityPrivileges = {
2855 {boost::beast::http::verb::get, {{"Login"}}},
2856 {boost::beast::http::verb::head, {{"Login"}}},
2857 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2858 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2859 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2860 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2861 }
2862
2863 private:
2864 void doGet(crow::Response &res, const crow::Request &req,
2865 const std::vector<std::string> &params) override
2866 {
2867 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2868 if (params.size() != 1)
2869 {
2870 messages::internalError(asyncResp->res);
2871 return;
2872 }
2873
2874 const std::string &targetID = params[0];
2875
2876 size_t bootPos = targetID.find('B');
2877 if (bootPos == std::string::npos)
2878 {
2879 // Requested ID was not found
2880 messages::resourceMissingAtURI(asyncResp->res, targetID);
2881 return;
2882 }
2883 std::string_view bootIndexStr(targetID);
2884 bootIndexStr.remove_prefix(bootPos + 1);
2885 uint16_t bootIndex = 0;
2886 uint64_t codeIndex = 0;
2887 size_t dashPos = bootIndexStr.find('-');
2888
2889 if (dashPos == std::string::npos)
2890 {
2891 return;
2892 }
2893 std::string_view codeIndexStr(bootIndexStr);
2894 bootIndexStr.remove_suffix(dashPos);
2895 codeIndexStr.remove_prefix(dashPos + 1);
2896
2897 bootIndex = static_cast<uint16_t>(
2898 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2899 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2900 if (bootIndex == 0 || codeIndex == 0)
2901 {
2902 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2903 << params[0];
2904 }
2905
2906 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2907 asyncResp->res.jsonValue["@odata.context"] =
2908 "/redfish/v1/$metadata#LogEntry.LogEntry";
2909 asyncResp->res.jsonValue["@odata.id"] =
2910 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2911 "Entries";
2912 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2913 asyncResp->res.jsonValue["Description"] =
2914 "Collection of POST Code Log Entries";
2915 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2916 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2917
2918 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2919 }
2920};
2921
Ed Tanous1da66f72018-07-27 16:13:37 -07002922} // namespace redfish