blob: 4d4a360e32e8ab68670c992c1b25019ac601f361 [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070030#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070031#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070032#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080033#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070034
35namespace redfish
36{
37
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070038constexpr char const *crashdumpObject = "com.intel.crashdump";
39constexpr char const *crashdumpPath = "/com/intel/crashdump";
40constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
41constexpr char const *crashdumpInterface = "com.intel.crashdump";
42constexpr char const *deleteAllInterface =
43 "xyz.openbmc_project.Collection.DeleteAll";
44constexpr char const *crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070045 "com.intel.crashdump.OnDemand";
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070046constexpr char const *crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070047 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070048
Jason M. Bills4851d452019-03-28 11:27:48 -070049namespace message_registries
50{
51static const Message *getMessageFromRegistry(
52 const std::string &messageKey,
53 const boost::beast::span<const MessageEntry> registry)
54{
55 boost::beast::span<const MessageEntry>::const_iterator messageIt =
56 std::find_if(registry.cbegin(), registry.cend(),
57 [&messageKey](const MessageEntry &messageEntry) {
58 return !std::strcmp(messageEntry.first,
59 messageKey.c_str());
60 });
61 if (messageIt != registry.cend())
62 {
63 return &messageIt->second;
64 }
65
66 return nullptr;
67}
68
69static const Message *getMessage(const std::string_view &messageID)
70{
71 // Redfish MessageIds are in the form
72 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
73 // the right Message
74 std::vector<std::string> fields;
75 fields.reserve(4);
76 boost::split(fields, messageID, boost::is_any_of("."));
77 std::string &registryName = fields[0];
78 std::string &messageKey = fields[3];
79
80 // Find the right registry and check it for the MessageKey
81 if (std::string(base::header.registryPrefix) == registryName)
82 {
83 return getMessageFromRegistry(
84 messageKey, boost::beast::span<const MessageEntry>(base::registry));
85 }
86 if (std::string(openbmc::header.registryPrefix) == registryName)
87 {
88 return getMessageFromRegistry(
89 messageKey,
90 boost::beast::span<const MessageEntry>(openbmc::registry));
91 }
92 return nullptr;
93}
94} // namespace message_registries
95
James Feistf6150402019-01-08 10:36:20 -080096namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070097
Andrew Geisslercb92c032018-08-17 07:56:14 -070098using GetManagedPropertyType = boost::container::flat_map<
99 std::string,
100 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
101 int32_t, uint32_t, int64_t, uint64_t, double>>;
102
103using GetManagedObjectsType = boost::container::flat_map<
104 sdbusplus::message::object_path,
105 boost::container::flat_map<std::string, GetManagedPropertyType>>;
106
107inline std::string translateSeverityDbusToRedfish(const std::string &s)
108{
109 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
110 {
111 return "Critical";
112 }
113 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
114 {
115 return "Critical";
116 }
117 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
118 {
119 return "OK";
120 }
121 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
122 {
123 return "Critical";
124 }
125 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
126 {
127 return "Critical";
128 }
129 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
130 {
131 return "OK";
132 }
133 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
134 {
135 return "OK";
136 }
137 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
138 {
139 return "Warning";
140 }
141 return "";
142}
143
Jason M. Bills16428a12018-11-02 12:42:29 -0700144static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800145 const std::string_view &field,
146 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700147{
148 const char *data = nullptr;
149 size_t length = 0;
150 int ret = 0;
151 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700152 ret = sd_journal_get_data(journal, field.data(),
153 reinterpret_cast<const void **>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700154 if (ret < 0)
155 {
156 return ret;
157 }
Ed Tanous39e77502019-03-04 17:35:53 -0800158 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700159 // Only use the content after the "=" character.
160 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
161 return ret;
162}
163
164static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800165 const std::string_view &field, const int &base,
Ed Tanous271584a2019-07-09 16:24:22 -0700166 long int &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700167{
168 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800169 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700170 // Get the metadata from the requested field of the journal entry
171 ret = getJournalMetadata(journal, field, metadata);
172 if (ret < 0)
173 {
174 return ret;
175 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000176 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700177 return ret;
178}
179
ZhikuiRena3316fc2020-01-29 14:58:08 -0800180static bool getTimestampStr(const uint64_t usecSinceEpoch,
181 std::string &entryTimestamp)
Jason M. Bills16428a12018-11-02 12:42:29 -0700182{
ZhikuiRena3316fc2020-01-29 14:58:08 -0800183 time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000);
Jason M. Bills16428a12018-11-02 12:42:29 -0700184 struct tm *loctime = localtime(&t);
185 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700186 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700187 {
188 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
189 }
190 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800191 std::string_view t1(entryTime);
192 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700193 if (t1.size() > 2 && t2.size() > 2)
194 {
195 t1.remove_suffix(2);
196 t2.remove_prefix(t2.size() - 2);
197 }
Ed Tanous39e77502019-03-04 17:35:53 -0800198 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700199 return true;
200}
201
ZhikuiRena3316fc2020-01-29 14:58:08 -0800202static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
203{
204 int ret = 0;
205 uint64_t timestamp = 0;
206 ret = sd_journal_get_realtime_usec(journal, &timestamp);
207 if (ret < 0)
208 {
209 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
210 << strerror(-ret);
211 return false;
212 }
213 return getTimestampStr(timestamp, entryTimestamp);
214}
215
Jason M. Bills16428a12018-11-02 12:42:29 -0700216static bool getSkipParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700217 uint64_t &skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700218{
219 char *skipParam = req.urlParams.get("$skip");
220 if (skipParam != nullptr)
221 {
222 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700223 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700224 if (*skipParam == '\0' || *ptr != '\0')
225 {
226
227 messages::queryParameterValueTypeError(res, std::string(skipParam),
228 "$skip");
229 return false;
230 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700231 }
232 return true;
233}
234
Ed Tanous271584a2019-07-09 16:24:22 -0700235static constexpr const uint64_t maxEntriesPerPage = 1000;
Jason M. Bills16428a12018-11-02 12:42:29 -0700236static bool getTopParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700237 uint64_t &top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700238{
239 char *topParam = req.urlParams.get("$top");
240 if (topParam != nullptr)
241 {
242 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700243 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700244 if (*topParam == '\0' || *ptr != '\0')
245 {
246 messages::queryParameterValueTypeError(res, std::string(topParam),
247 "$top");
248 return false;
249 }
Ed Tanous271584a2019-07-09 16:24:22 -0700250 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700251 {
252
253 messages::queryParameterOutOfRange(
254 res, std::to_string(top), "$top",
255 "1-" + std::to_string(maxEntriesPerPage));
256 return false;
257 }
258 }
259 return true;
260}
261
Jason M. Billse85d6b12019-07-29 17:01:15 -0700262static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
263 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700264{
265 int ret = 0;
266 static uint64_t prevTs = 0;
267 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700268 if (firstEntry)
269 {
270 prevTs = 0;
271 }
272
Jason M. Bills16428a12018-11-02 12:42:29 -0700273 // Get the entry timestamp
274 uint64_t curTs = 0;
275 ret = sd_journal_get_realtime_usec(journal, &curTs);
276 if (ret < 0)
277 {
278 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
279 << strerror(-ret);
280 return false;
281 }
282 // If the timestamp isn't unique, increment the index
283 if (curTs == prevTs)
284 {
285 index++;
286 }
287 else
288 {
289 // Otherwise, reset it
290 index = 0;
291 }
292 // Save the timestamp
293 prevTs = curTs;
294
295 entryID = std::to_string(curTs);
296 if (index > 0)
297 {
298 entryID += "_" + std::to_string(index);
299 }
300 return true;
301}
302
Jason M. Billse85d6b12019-07-29 17:01:15 -0700303static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
304 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700305{
Ed Tanous271584a2019-07-09 16:24:22 -0700306 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700307 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700308 if (firstEntry)
309 {
310 prevTs = 0;
311 }
312
Jason M. Bills95820182019-04-22 16:25:34 -0700313 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700314 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700315 std::tm timeStruct = {};
316 std::istringstream entryStream(logEntry);
317 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
318 {
319 curTs = std::mktime(&timeStruct);
320 }
321 // If the timestamp isn't unique, increment the index
322 if (curTs == prevTs)
323 {
324 index++;
325 }
326 else
327 {
328 // Otherwise, reset it
329 index = 0;
330 }
331 // Save the timestamp
332 prevTs = curTs;
333
334 entryID = std::to_string(curTs);
335 if (index > 0)
336 {
337 entryID += "_" + std::to_string(index);
338 }
339 return true;
340}
341
Jason M. Bills16428a12018-11-02 12:42:29 -0700342static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
Ed Tanous271584a2019-07-09 16:24:22 -0700343 uint64_t &timestamp, uint64_t &index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700344{
345 if (entryID.empty())
346 {
347 return false;
348 }
349 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800350 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700351
352 auto underscorePos = tsStr.find("_");
353 if (underscorePos != tsStr.npos)
354 {
355 // Timestamp has an index
356 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800357 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700358 indexStr.remove_prefix(underscorePos + 1);
359 std::size_t pos;
360 try
361 {
Ed Tanous39e77502019-03-04 17:35:53 -0800362 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700363 }
Ed Tanous271584a2019-07-09 16:24:22 -0700364 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700365 {
366 messages::resourceMissingAtURI(res, entryID);
367 return false;
368 }
Ed Tanous271584a2019-07-09 16:24:22 -0700369 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700370 {
371 messages::resourceMissingAtURI(res, entryID);
372 return false;
373 }
374 if (pos != indexStr.size())
375 {
376 messages::resourceMissingAtURI(res, entryID);
377 return false;
378 }
379 }
380 // Timestamp has no index
381 std::size_t pos;
382 try
383 {
Ed Tanous39e77502019-03-04 17:35:53 -0800384 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700385 }
Ed Tanous271584a2019-07-09 16:24:22 -0700386 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700387 {
388 messages::resourceMissingAtURI(res, entryID);
389 return false;
390 }
Ed Tanous271584a2019-07-09 16:24:22 -0700391 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700392 {
393 messages::resourceMissingAtURI(res, entryID);
394 return false;
395 }
396 if (pos != tsStr.size())
397 {
398 messages::resourceMissingAtURI(res, entryID);
399 return false;
400 }
401 return true;
402}
403
Jason M. Bills95820182019-04-22 16:25:34 -0700404static bool
405 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
406{
407 static const std::filesystem::path redfishLogDir = "/var/log";
408 static const std::string redfishLogFilename = "redfish";
409
410 // Loop through the directory looking for redfish log files
411 for (const std::filesystem::directory_entry &dirEnt :
412 std::filesystem::directory_iterator(redfishLogDir))
413 {
414 // If we find a redfish log file, save the path
415 std::string filename = dirEnt.path().filename();
416 if (boost::starts_with(filename, redfishLogFilename))
417 {
418 redfishLogFiles.emplace_back(redfishLogDir / filename);
419 }
420 }
421 // As the log files rotate, they are appended with a ".#" that is higher for
422 // the older logs. Since we don't expect more than 10 log files, we
423 // can just sort the list to get them in order from newest to oldest
424 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
425
426 return !redfishLogFiles.empty();
427}
428
Johnathan Mantey043a0532020-03-10 17:15:28 -0700429static void ParseCrashdumpParameters(
430 const std::vector<std::pair<std::string, VariantType>> &params,
431 std::string &filename, std::string &timestamp, std::string &logfile)
432{
433 for (auto property : params)
434 {
435 if (property.first == "Timestamp")
436 {
437 const std::string *value =
438 sdbusplus::message::variant_ns::get_if<std::string>(
439 &property.second);
440 if (value != nullptr)
441 {
442 timestamp = *value;
443 }
444 }
445 else if (property.first == "Filename")
446 {
447 const std::string *value =
448 sdbusplus::message::variant_ns::get_if<std::string>(
449 &property.second);
450 if (value != nullptr)
451 {
452 filename = *value;
453 }
454 }
455 else if (property.first == "Log")
456 {
457 const std::string *value =
458 sdbusplus::message::variant_ns::get_if<std::string>(
459 &property.second);
460 if (value != nullptr)
461 {
462 logfile = *value;
463 }
464 }
465 }
466}
467
ZhikuiRena3316fc2020-01-29 14:58:08 -0800468constexpr char const *postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800469class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700470{
471 public:
472 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800473 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800474 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800475 {
476 entityPrivileges = {
477 {boost::beast::http::verb::get, {{"Login"}}},
478 {boost::beast::http::verb::head, {{"Login"}}},
479 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
480 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
481 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
482 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
483 }
484
485 private:
486 /**
487 * Functions triggers appropriate requests on DBus
488 */
489 void doGet(crow::Response &res, const crow::Request &req,
490 const std::vector<std::string> &params) override
491 {
492 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800493 // Collections don't include the static data added by SubRoute because
494 // it has a duplicate entry for members
495 asyncResp->res.jsonValue["@odata.type"] =
496 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800497 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800498 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800499 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
500 asyncResp->res.jsonValue["Description"] =
501 "Collection of LogServices for this Computer System";
502 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
503 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800504 logServiceArray.push_back(
505 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800506#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
507 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500508 {{"@odata.id",
509 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800510#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800511 asyncResp->res.jsonValue["Members@odata.count"] =
512 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800513
514 crow::connections::systemBus->async_method_call(
515 [asyncResp](const boost::system::error_code ec,
516 const std::vector<std::string> &subtreePath) {
517 if (ec)
518 {
519 BMCWEB_LOG_ERROR << ec;
520 return;
521 }
522
523 for (auto &pathStr : subtreePath)
524 {
525 if (pathStr.find("PostCode") != std::string::npos)
526 {
527 nlohmann::json &logServiceArray =
528 asyncResp->res.jsonValue["Members"];
529 logServiceArray.push_back(
530 {{"@odata.id", "/redfish/v1/Systems/system/"
531 "LogServices/PostCodes"}});
532 asyncResp->res.jsonValue["Members@odata.count"] =
533 logServiceArray.size();
534 return;
535 }
536 }
537 },
538 "xyz.openbmc_project.ObjectMapper",
539 "/xyz/openbmc_project/object_mapper",
540 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
541 std::array<const char *, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800542 }
543};
544
545class EventLogService : public Node
546{
547 public:
548 template <typename CrowApp>
549 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800550 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800551 {
552 entityPrivileges = {
553 {boost::beast::http::verb::get, {{"Login"}}},
554 {boost::beast::http::verb::head, {{"Login"}}},
555 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
556 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
557 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
558 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
559 }
560
561 private:
562 void doGet(crow::Response &res, const crow::Request &req,
563 const std::vector<std::string> &params) override
564 {
565 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
566
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800567 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800568 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800569 asyncResp->res.jsonValue["@odata.type"] =
570 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800571 asyncResp->res.jsonValue["Name"] = "Event Log Service";
572 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -0500573 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800574 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
575 asyncResp->res.jsonValue["Entries"] = {
576 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800577 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500578 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
579
580 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
581 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700582 }
583};
584
Tim Lee1f56a3a2019-10-09 10:17:57 +0800585class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700586{
587 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800588 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700589 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
590 "LogService.ClearLog/")
591 {
592 entityPrivileges = {
593 {boost::beast::http::verb::get, {{"Login"}}},
594 {boost::beast::http::verb::head, {{"Login"}}},
595 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
596 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
597 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
598 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
599 }
600
601 private:
602 void doPost(crow::Response &res, const crow::Request &req,
603 const std::vector<std::string> &params) override
604 {
605 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
606
607 // Clear the EventLog by deleting the log files
608 std::vector<std::filesystem::path> redfishLogFiles;
609 if (getRedfishLogFiles(redfishLogFiles))
610 {
611 for (const std::filesystem::path &file : redfishLogFiles)
612 {
613 std::error_code ec;
614 std::filesystem::remove(file, ec);
615 }
616 }
617
618 // Reload rsyslog so it knows to start new log files
619 crow::connections::systemBus->async_method_call(
620 [asyncResp](const boost::system::error_code ec) {
621 if (ec)
622 {
623 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
624 messages::internalError(asyncResp->res);
625 return;
626 }
627
628 messages::success(asyncResp->res);
629 },
630 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
631 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
632 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800633 }
634};
635
Jason M. Bills95820182019-04-22 16:25:34 -0700636static int fillEventLogEntryJson(const std::string &logEntryID,
637 const std::string logEntry,
638 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800639{
Jason M. Bills95820182019-04-22 16:25:34 -0700640 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700641 // First get the Timestamp
642 size_t space = logEntry.find_first_of(" ");
643 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700644 {
645 return 1;
646 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700647 std::string timestamp = logEntry.substr(0, space);
648 // Then get the log contents
649 size_t entryStart = logEntry.find_first_not_of(" ", space);
650 if (entryStart == std::string::npos)
651 {
652 return 1;
653 }
654 std::string_view entry(logEntry);
655 entry.remove_prefix(entryStart);
656 // Use split to separate the entry into its fields
657 std::vector<std::string> logEntryFields;
658 boost::split(logEntryFields, entry, boost::is_any_of(","),
659 boost::token_compress_on);
660 // We need at least a MessageId to be valid
661 if (logEntryFields.size() < 1)
662 {
663 return 1;
664 }
665 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700666
Jason M. Bills4851d452019-03-28 11:27:48 -0700667 // Get the Message from the MessageRegistry
668 const message_registries::Message *message =
669 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800670
Jason M. Bills4851d452019-03-28 11:27:48 -0700671 std::string msg;
672 std::string severity;
673 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800674 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700675 msg = message->message;
676 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800677 }
678
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700679 // Get the MessageArgs from the log if there are any
680 boost::beast::span<std::string> messageArgs;
681 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700682 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700683 std::string &messageArgsStart = logEntryFields[1];
684 // If the first string is empty, assume there are no MessageArgs
685 std::size_t messageArgsSize = 0;
686 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700687 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700688 messageArgsSize = logEntryFields.size() - 1;
689 }
690
691 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
692
693 // Fill the MessageArgs into the Message
694 int i = 0;
695 for (const std::string &messageArg : messageArgs)
696 {
697 std::string argStr = "%" + std::to_string(++i);
698 size_t argPos = msg.find(argStr);
699 if (argPos != std::string::npos)
700 {
701 msg.replace(argPos, argStr.length(), messageArg);
702 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700703 }
704 }
705
Jason M. Bills95820182019-04-22 16:25:34 -0700706 // Get the Created time from the timestamp. The log timestamp is in RFC3339
707 // format which matches the Redfish format except for the fractional seconds
708 // between the '.' and the '+', so just remove them.
709 std::size_t dot = timestamp.find_first_of(".");
710 std::size_t plus = timestamp.find_first_of("+");
711 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800712 {
Jason M. Bills95820182019-04-22 16:25:34 -0700713 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800714 }
715
716 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700717 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700718 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800719 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700720 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700721 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800722 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700723 {"Id", logEntryID},
724 {"Message", std::move(msg)},
725 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800726 {"MessageArgs", std::move(messageArgs)},
727 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700728 {"Severity", std::move(severity)},
729 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800730 return 0;
731}
732
Anthony Wilson27062602019-04-22 02:10:09 -0500733class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800734{
735 public:
736 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500737 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800738 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800739 {
740 entityPrivileges = {
741 {boost::beast::http::verb::get, {{"Login"}}},
742 {boost::beast::http::verb::head, {{"Login"}}},
743 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
744 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
745 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
746 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
747 }
748
749 private:
750 void doGet(crow::Response &res, const crow::Request &req,
751 const std::vector<std::string> &params) override
752 {
753 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700754 uint64_t skip = 0;
755 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800756 if (!getSkipParam(asyncResp->res, req, skip))
757 {
758 return;
759 }
760 if (!getTopParam(asyncResp->res, req, top))
761 {
762 return;
763 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800764 // Collections don't include the static data added by SubRoute because
765 // it has a duplicate entry for members
766 asyncResp->res.jsonValue["@odata.type"] =
767 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800768 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800769 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800770 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
771 asyncResp->res.jsonValue["Description"] =
772 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700773
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800774 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
775 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700776 // Go through the log files and create a unique ID for each entry
777 std::vector<std::filesystem::path> redfishLogFiles;
778 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000779 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700780 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700781
782 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700783 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
784 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800785 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700786 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700787 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800788 {
789 continue;
790 }
791
Jason M. Billse85d6b12019-07-29 17:01:15 -0700792 // Reset the unique ID on the first entry
793 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700794 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800795 {
Jason M. Bills95820182019-04-22 16:25:34 -0700796 entryCount++;
797 // Handle paging using skip (number of entries to skip from the
798 // start) and top (number of entries to display)
799 if (entryCount <= skip || entryCount > skip + top)
800 {
801 continue;
802 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800803
Jason M. Bills95820182019-04-22 16:25:34 -0700804 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700805 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700806 {
807 continue;
808 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800809
Jason M. Billse85d6b12019-07-29 17:01:15 -0700810 if (firstEntry)
811 {
812 firstEntry = false;
813 }
814
Jason M. Bills95820182019-04-22 16:25:34 -0700815 logEntryArray.push_back({});
816 nlohmann::json &bmcLogEntry = logEntryArray.back();
817 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
818 {
819 messages::internalError(asyncResp->res);
820 return;
821 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800822 }
823 }
824 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
825 if (skip + top < entryCount)
826 {
827 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700828 "/redfish/v1/Systems/system/LogServices/EventLog/"
829 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800830 std::to_string(skip + top);
831 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500832 }
833};
834
Jason M. Bills897967d2019-07-29 17:05:30 -0700835class JournalEventLogEntry : public Node
836{
837 public:
838 JournalEventLogEntry(CrowApp &app) :
839 Node(app,
840 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
841 std::string())
842 {
843 entityPrivileges = {
844 {boost::beast::http::verb::get, {{"Login"}}},
845 {boost::beast::http::verb::head, {{"Login"}}},
846 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
847 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
848 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
849 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
850 }
851
852 private:
853 void doGet(crow::Response &res, const crow::Request &req,
854 const std::vector<std::string> &params) override
855 {
856 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
857 if (params.size() != 1)
858 {
859 messages::internalError(asyncResp->res);
860 return;
861 }
862 const std::string &targetID = params[0];
863
864 // Go through the log files and check the unique ID for each entry to
865 // find the target entry
866 std::vector<std::filesystem::path> redfishLogFiles;
867 getRedfishLogFiles(redfishLogFiles);
868 std::string logEntry;
869
870 // Oldest logs are in the last file, so start there and loop backwards
871 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
872 it++)
873 {
874 std::ifstream logStream(*it);
875 if (!logStream.is_open())
876 {
877 continue;
878 }
879
880 // Reset the unique ID on the first entry
881 bool firstEntry = true;
882 while (std::getline(logStream, logEntry))
883 {
884 std::string idStr;
885 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
886 {
887 continue;
888 }
889
890 if (firstEntry)
891 {
892 firstEntry = false;
893 }
894
895 if (idStr == targetID)
896 {
897 if (fillEventLogEntryJson(idStr, logEntry,
898 asyncResp->res.jsonValue) != 0)
899 {
900 messages::internalError(asyncResp->res);
901 return;
902 }
903 return;
904 }
905 }
906 }
907 // Requested ID was not found
908 messages::resourceMissingAtURI(asyncResp->res, targetID);
909 }
910};
911
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500912class DBusEventLogEntryCollection : public Node
913{
914 public:
915 template <typename CrowApp>
916 DBusEventLogEntryCollection(CrowApp &app) :
917 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
918 {
919 entityPrivileges = {
920 {boost::beast::http::verb::get, {{"Login"}}},
921 {boost::beast::http::verb::head, {{"Login"}}},
922 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
923 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
924 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
925 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
926 }
927
928 private:
929 void doGet(crow::Response &res, const crow::Request &req,
930 const std::vector<std::string> &params) override
931 {
932 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
933
934 // Collections don't include the static data added by SubRoute because
935 // it has a duplicate entry for members
936 asyncResp->res.jsonValue["@odata.type"] =
937 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500938 asyncResp->res.jsonValue["@odata.id"] =
939 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
940 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
941 asyncResp->res.jsonValue["Description"] =
942 "Collection of System Event Log Entries";
943
Andrew Geisslercb92c032018-08-17 07:56:14 -0700944 // DBus implementation of EventLog/Entries
945 // Make call to Logging Service to find all log entry objects
946 crow::connections::systemBus->async_method_call(
947 [asyncResp](const boost::system::error_code ec,
948 GetManagedObjectsType &resp) {
949 if (ec)
950 {
951 // TODO Handle for specific error code
952 BMCWEB_LOG_ERROR
953 << "getLogEntriesIfaceData resp_handler got error "
954 << ec;
955 messages::internalError(asyncResp->res);
956 return;
957 }
958 nlohmann::json &entriesArray =
959 asyncResp->res.jsonValue["Members"];
960 entriesArray = nlohmann::json::array();
961 for (auto &objectPath : resp)
962 {
963 for (auto &interfaceMap : objectPath.second)
964 {
965 if (interfaceMap.first !=
966 "xyz.openbmc_project.Logging.Entry")
967 {
968 BMCWEB_LOG_DEBUG << "Bailing early on "
969 << interfaceMap.first;
970 continue;
971 }
972 entriesArray.push_back({});
973 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -0700974 uint32_t *id = nullptr;
975 std::time_t timestamp{};
976 std::string *severity = nullptr;
977 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700978 for (auto &propertyMap : interfaceMap.second)
979 {
980 if (propertyMap.first == "Id")
981 {
982 id = sdbusplus::message::variant_ns::get_if<
983 uint32_t>(&propertyMap.second);
984 if (id == nullptr)
985 {
986 messages::propertyMissing(asyncResp->res,
987 "Id");
988 }
989 }
990 else if (propertyMap.first == "Timestamp")
991 {
992 const uint64_t *millisTimeStamp =
993 std::get_if<uint64_t>(&propertyMap.second);
994 if (millisTimeStamp == nullptr)
995 {
996 messages::propertyMissing(asyncResp->res,
997 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -0700998 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700999 }
1000 // Retrieve Created property with format:
1001 // yyyy-mm-ddThh:mm:ss
1002 std::chrono::milliseconds chronoTimeStamp(
1003 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001004 timestamp = std::chrono::duration_cast<
1005 std::chrono::duration<int>>(
1006 chronoTimeStamp)
1007 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001008 }
1009 else if (propertyMap.first == "Severity")
1010 {
1011 severity = std::get_if<std::string>(
1012 &propertyMap.second);
1013 if (severity == nullptr)
1014 {
1015 messages::propertyMissing(asyncResp->res,
1016 "Severity");
1017 }
1018 }
1019 else if (propertyMap.first == "Message")
1020 {
1021 message = std::get_if<std::string>(
1022 &propertyMap.second);
1023 if (message == nullptr)
1024 {
1025 messages::propertyMissing(asyncResp->res,
1026 "Message");
1027 }
1028 }
1029 }
1030 thisEntry = {
1031 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001032 {"@odata.id",
1033 "/redfish/v1/Systems/system/LogServices/EventLog/"
1034 "Entries/" +
1035 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001036 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001037 {"Id", std::to_string(*id)},
1038 {"Message", *message},
1039 {"EntryType", "Event"},
1040 {"Severity",
1041 translateSeverityDbusToRedfish(*severity)},
1042 {"Created", crow::utility::getDateTime(timestamp)}};
1043 }
1044 }
1045 std::sort(entriesArray.begin(), entriesArray.end(),
1046 [](const nlohmann::json &left,
1047 const nlohmann::json &right) {
1048 return (left["Id"] <= right["Id"]);
1049 });
1050 asyncResp->res.jsonValue["Members@odata.count"] =
1051 entriesArray.size();
1052 },
1053 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1054 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001055 }
1056};
1057
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001058class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001059{
1060 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001061 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001062 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001063 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1064 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001065 {
1066 entityPrivileges = {
1067 {boost::beast::http::verb::get, {{"Login"}}},
1068 {boost::beast::http::verb::head, {{"Login"}}},
1069 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1070 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1071 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1072 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1073 }
1074
1075 private:
1076 void doGet(crow::Response &res, const crow::Request &req,
1077 const std::vector<std::string> &params) override
1078 {
1079 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001080 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001081 {
1082 messages::internalError(asyncResp->res);
1083 return;
1084 }
Ed Tanous029573d2019-02-01 10:57:49 -08001085 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001086
Andrew Geisslercb92c032018-08-17 07:56:14 -07001087 // DBus implementation of EventLog/Entries
1088 // Make call to Logging Service to find all log entry objects
1089 crow::connections::systemBus->async_method_call(
1090 [asyncResp, entryID](const boost::system::error_code ec,
1091 GetManagedPropertyType &resp) {
1092 if (ec)
1093 {
1094 BMCWEB_LOG_ERROR
1095 << "EventLogEntry (DBus) resp_handler got error " << ec;
1096 messages::internalError(asyncResp->res);
1097 return;
1098 }
Ed Tanous66664f22019-10-11 13:05:49 -07001099 uint32_t *id = nullptr;
1100 std::time_t timestamp{};
1101 std::string *severity = nullptr;
1102 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001103 for (auto &propertyMap : resp)
1104 {
1105 if (propertyMap.first == "Id")
1106 {
1107 id = std::get_if<uint32_t>(&propertyMap.second);
1108 if (id == nullptr)
1109 {
1110 messages::propertyMissing(asyncResp->res, "Id");
1111 }
1112 }
1113 else if (propertyMap.first == "Timestamp")
1114 {
1115 const uint64_t *millisTimeStamp =
1116 std::get_if<uint64_t>(&propertyMap.second);
1117 if (millisTimeStamp == nullptr)
1118 {
1119 messages::propertyMissing(asyncResp->res,
1120 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001121 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001122 }
1123 // Retrieve Created property with format:
1124 // yyyy-mm-ddThh:mm:ss
1125 std::chrono::milliseconds chronoTimeStamp(
1126 *millisTimeStamp);
1127 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001128 std::chrono::duration_cast<
1129 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001130 .count();
1131 }
1132 else if (propertyMap.first == "Severity")
1133 {
1134 severity =
1135 std::get_if<std::string>(&propertyMap.second);
1136 if (severity == nullptr)
1137 {
1138 messages::propertyMissing(asyncResp->res,
1139 "Severity");
1140 }
1141 }
1142 else if (propertyMap.first == "Message")
1143 {
1144 message = std::get_if<std::string>(&propertyMap.second);
1145 if (message == nullptr)
1146 {
1147 messages::propertyMissing(asyncResp->res,
1148 "Message");
1149 }
1150 }
1151 }
Ed Tanous271584a2019-07-09 16:24:22 -07001152 if (id == nullptr || message == nullptr || severity == nullptr)
1153 {
1154 return;
1155 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001156 asyncResp->res.jsonValue = {
1157 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001158 {"@odata.id",
1159 "/redfish/v1/Systems/system/LogServices/EventLog/"
1160 "Entries/" +
1161 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001162 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001163 {"Id", std::to_string(*id)},
1164 {"Message", *message},
1165 {"EntryType", "Event"},
1166 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001167 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001168 },
1169 "xyz.openbmc_project.Logging",
1170 "/xyz/openbmc_project/logging/entry/" + entryID,
1171 "org.freedesktop.DBus.Properties", "GetAll",
1172 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001173 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001174
1175 void doDelete(crow::Response &res, const crow::Request &req,
1176 const std::vector<std::string> &params) override
1177 {
1178
1179 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1180
1181 auto asyncResp = std::make_shared<AsyncResp>(res);
1182
1183 if (params.size() != 1)
1184 {
1185 messages::internalError(asyncResp->res);
1186 return;
1187 }
1188 std::string entryID = params[0];
1189
1190 dbus::utility::escapePathForDbus(entryID);
1191
1192 // Process response from Logging service.
1193 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1194 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1195 if (ec)
1196 {
1197 // TODO Handle for specific error code
1198 BMCWEB_LOG_ERROR
1199 << "EventLogEntry (DBus) doDelete respHandler got error "
1200 << ec;
1201 asyncResp->res.result(
1202 boost::beast::http::status::internal_server_error);
1203 return;
1204 }
1205
1206 asyncResp->res.result(boost::beast::http::status::ok);
1207 };
1208
1209 // Make call to Logging service to request Delete Log
1210 crow::connections::systemBus->async_method_call(
1211 respHandler, "xyz.openbmc_project.Logging",
1212 "/xyz/openbmc_project/logging/entry/" + entryID,
1213 "xyz.openbmc_project.Object.Delete", "Delete");
1214 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001215};
1216
1217class BMCLogServiceCollection : public Node
1218{
1219 public:
1220 template <typename CrowApp>
1221 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001222 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001223 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001224 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001225 {boost::beast::http::verb::get, {{"Login"}}},
1226 {boost::beast::http::verb::head, {{"Login"}}},
1227 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1228 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1229 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1230 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001231 }
1232
1233 private:
1234 /**
1235 * Functions triggers appropriate requests on DBus
1236 */
1237 void doGet(crow::Response &res, const crow::Request &req,
1238 const std::vector<std::string> &params) override
1239 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001240 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001241 // Collections don't include the static data added by SubRoute because
1242 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001243 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001244 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001245 asyncResp->res.jsonValue["@odata.id"] =
1246 "/redfish/v1/Managers/bmc/LogServices";
1247 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1248 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001249 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001250 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1251 logServiceArray = nlohmann::json::array();
1252#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1253 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001254 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001255#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001256 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001257 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001258 }
1259};
1260
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001261class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001262{
1263 public:
1264 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001265 BMCJournalLogService(CrowApp &app) :
1266 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001267 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001268 entityPrivileges = {
1269 {boost::beast::http::verb::get, {{"Login"}}},
1270 {boost::beast::http::verb::head, {{"Login"}}},
1271 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1272 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1273 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1274 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1275 }
1276
1277 private:
1278 void doGet(crow::Response &res, const crow::Request &req,
1279 const std::vector<std::string> &params) override
1280 {
1281 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001282 asyncResp->res.jsonValue["@odata.type"] =
1283 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001284 asyncResp->res.jsonValue["@odata.id"] =
1285 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001286 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1287 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1288 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001289 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001290 asyncResp->res.jsonValue["Entries"] = {
1291 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001292 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001293 }
1294};
1295
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001296static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1297 sd_journal *journal,
1298 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001299{
1300 // Get the Log Entry contents
1301 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001302
Ed Tanous39e77502019-03-04 17:35:53 -08001303 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001304 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001305 if (ret < 0)
1306 {
1307 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1308 return 1;
1309 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001310
1311 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001312 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001313 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001314 if (ret < 0)
1315 {
1316 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001317 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001318
1319 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001320 std::string entryTimeStr;
1321 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001322 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001323 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001324 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001325
1326 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001327 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001328 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001329 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1330 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001331 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001332 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001333 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001334 {"EntryType", "Oem"},
1335 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001336 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001337 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001338 {"Created", std::move(entryTimeStr)}};
1339 return 0;
1340}
1341
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001342class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001343{
1344 public:
1345 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001346 BMCJournalLogEntryCollection(CrowApp &app) :
1347 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001348 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001349 entityPrivileges = {
1350 {boost::beast::http::verb::get, {{"Login"}}},
1351 {boost::beast::http::verb::head, {{"Login"}}},
1352 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1353 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1354 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1355 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1356 }
1357
1358 private:
1359 void doGet(crow::Response &res, const crow::Request &req,
1360 const std::vector<std::string> &params) override
1361 {
1362 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001363 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001364 uint64_t skip = 0;
1365 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001366 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001367 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001368 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001369 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001370 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001371 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001372 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001373 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001374 // Collections don't include the static data added by SubRoute because
1375 // it has a duplicate entry for members
1376 asyncResp->res.jsonValue["@odata.type"] =
1377 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001378 asyncResp->res.jsonValue["@odata.id"] =
1379 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001380 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001381 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001382 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1383 asyncResp->res.jsonValue["Description"] =
1384 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001385 asyncResp->res.jsonValue["@odata.id"] =
1386 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001387 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1388 logEntryArray = nlohmann::json::array();
1389
1390 // Go through the journal and use the timestamp to create a unique ID
1391 // for each entry
1392 sd_journal *journalTmp = nullptr;
1393 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1394 if (ret < 0)
1395 {
1396 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001397 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001398 return;
1399 }
1400 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1401 journalTmp, sd_journal_close);
1402 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001403 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001404 // Reset the unique ID on the first entry
1405 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001406 SD_JOURNAL_FOREACH(journal.get())
1407 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001408 entryCount++;
1409 // Handle paging using skip (number of entries to skip from the
1410 // start) and top (number of entries to display)
1411 if (entryCount <= skip || entryCount > skip + top)
1412 {
1413 continue;
1414 }
1415
Jason M. Bills16428a12018-11-02 12:42:29 -07001416 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001417 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001418 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001419 continue;
1420 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001421
Jason M. Billse85d6b12019-07-29 17:01:15 -07001422 if (firstEntry)
1423 {
1424 firstEntry = false;
1425 }
1426
Jason M. Billse1f26342018-07-18 12:12:00 -07001427 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001428 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1429 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1430 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001431 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001432 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001433 return;
1434 }
1435 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001436 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1437 if (skip + top < entryCount)
1438 {
1439 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001440 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001441 std::to_string(skip + top);
1442 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001443 }
1444};
1445
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001446class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001447{
1448 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001449 BMCJournalLogEntry(CrowApp &app) :
1450 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001451 std::string())
1452 {
1453 entityPrivileges = {
1454 {boost::beast::http::verb::get, {{"Login"}}},
1455 {boost::beast::http::verb::head, {{"Login"}}},
1456 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1457 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1458 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1459 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1460 }
1461
1462 private:
1463 void doGet(crow::Response &res, const crow::Request &req,
1464 const std::vector<std::string> &params) override
1465 {
1466 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1467 if (params.size() != 1)
1468 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001469 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001470 return;
1471 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001472 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001473 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001474 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001475 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001476 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001477 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001478 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001479 }
1480
1481 sd_journal *journalTmp = nullptr;
1482 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1483 if (ret < 0)
1484 {
1485 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001486 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001487 return;
1488 }
1489 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1490 journalTmp, sd_journal_close);
1491 journalTmp = nullptr;
1492 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001493 // tracking the unique ID
1494 std::string idStr;
1495 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001496 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001497 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001498 {
1499 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001500 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1501 {
1502 messages::internalError(asyncResp->res);
1503 return;
1504 }
1505 if (firstEntry)
1506 {
1507 firstEntry = false;
1508 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001509 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001510 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001511 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001512 {
1513 messages::resourceMissingAtURI(asyncResp->res, entryID);
1514 return;
1515 }
1516
1517 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1518 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001519 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001520 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001521 return;
1522 }
1523 }
1524};
1525
Jason M. Bills424c4172019-03-21 13:50:33 -07001526class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001527{
1528 public:
1529 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001530 CrashdumpService(CrowApp &app) :
1531 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001532 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001533 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001534 {boost::beast::http::verb::get, {{"Login"}}},
1535 {boost::beast::http::verb::head, {{"Login"}}},
1536 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1537 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1538 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1539 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001540 }
1541
1542 private:
1543 /**
1544 * Functions triggers appropriate requests on DBus
1545 */
1546 void doGet(crow::Response &res, const crow::Request &req,
1547 const std::vector<std::string> &params) override
1548 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001549 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001550 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001551 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001552 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001553 asyncResp->res.jsonValue["@odata.type"] =
1554 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001555 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1556 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1557 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001558 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1559 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001560 asyncResp->res.jsonValue["Entries"] = {
1561 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001562 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001563 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001564 {"#LogService.ClearLog",
1565 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1566 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001567 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001568 {{"#Crashdump.OnDemand",
1569 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1570 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001571
1572#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001573 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001574 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001575 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1576 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001577#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001578 }
1579};
1580
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001581class CrashdumpClear : public Node
1582{
1583 public:
1584 CrashdumpClear(CrowApp &app) :
1585 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1586 "LogService.ClearLog/")
1587 {
1588 entityPrivileges = {
1589 {boost::beast::http::verb::get, {{"Login"}}},
1590 {boost::beast::http::verb::head, {{"Login"}}},
1591 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1592 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1593 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1594 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1595 }
1596
1597 private:
1598 void doPost(crow::Response &res, const crow::Request &req,
1599 const std::vector<std::string> &params) override
1600 {
1601 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1602
1603 crow::connections::systemBus->async_method_call(
1604 [asyncResp](const boost::system::error_code ec,
1605 const std::string &resp) {
1606 if (ec)
1607 {
1608 messages::internalError(asyncResp->res);
1609 return;
1610 }
1611 messages::success(asyncResp->res);
1612 },
1613 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
1614 }
1615};
1616
Jason M. Billse855dd22019-10-08 11:37:48 -07001617static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1618 const std::string &logID,
1619 nlohmann::json &logEntryJson)
1620{
Johnathan Mantey043a0532020-03-10 17:15:28 -07001621 auto getStoredLogCallback =
1622 [asyncResp, logID, &logEntryJson](
1623 const boost::system::error_code ec,
1624 const std::vector<std::pair<std::string, VariantType>> &params) {
1625 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001626 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07001627 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1628 if (ec.value() ==
1629 boost::system::linux_error::bad_request_descriptor)
1630 {
1631 messages::resourceNotFound(asyncResp->res, "LogEntry",
1632 logID);
1633 }
1634 else
1635 {
1636 messages::internalError(asyncResp->res);
1637 }
1638 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001639 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001640
Johnathan Mantey043a0532020-03-10 17:15:28 -07001641 std::string timestamp{};
1642 std::string filename{};
1643 std::string logfile{};
1644 ParseCrashdumpParameters(params, filename, timestamp, logfile);
1645
1646 if (filename.empty() || timestamp.empty())
1647 {
1648 messages::resourceMissingAtURI(asyncResp->res, logID);
1649 return;
1650 }
1651
1652 std::string crashdumpURI =
1653 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1654 logID + "/" + filename;
1655 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1656 {"@odata.id", "/redfish/v1/Systems/system/"
1657 "LogServices/Crashdump/Entries/" +
1658 logID},
1659 {"Name", "CPU Crashdump"},
1660 {"Id", logID},
1661 {"EntryType", "Oem"},
1662 {"OemRecordFormat", "Crashdump URI"},
1663 {"Message", std::move(crashdumpURI)},
1664 {"Created", std::move(timestamp)}};
1665 };
Jason M. Billse855dd22019-10-08 11:37:48 -07001666 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001667 std::move(getStoredLogCallback), crashdumpObject,
1668 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07001669 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07001670}
1671
Jason M. Bills424c4172019-03-21 13:50:33 -07001672class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001673{
1674 public:
1675 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001676 CrashdumpEntryCollection(CrowApp &app) :
1677 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001678 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001679 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001680 {boost::beast::http::verb::get, {{"Login"}}},
1681 {boost::beast::http::verb::head, {{"Login"}}},
1682 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1683 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1684 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1685 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001686 }
1687
1688 private:
1689 /**
1690 * Functions triggers appropriate requests on DBus
1691 */
1692 void doGet(crow::Response &res, const crow::Request &req,
1693 const std::vector<std::string> &params) override
1694 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001695 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001696 // Collections don't include the static data added by SubRoute because
1697 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001698 auto getLogEntriesCallback = [asyncResp](
1699 const boost::system::error_code ec,
1700 const std::vector<std::string> &resp) {
1701 if (ec)
1702 {
1703 if (ec.value() !=
1704 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001705 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001706 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1707 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001708 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001709 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001710 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001711 }
1712 asyncResp->res.jsonValue["@odata.type"] =
1713 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001714 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001715 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07001716 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001717 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001718 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001719 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1720 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07001721 std::vector<std::string> logIDs;
1722 // Get the list of log entries and build up an empty array big
1723 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07001724 for (const std::string &objpath : resp)
1725 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001726 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07001727 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07001728 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07001729 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001730 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07001731 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001732 logIDs.emplace_back(objpath.substr(lastPos + 1));
1733
1734 // Add a space for the log entry to the array
1735 logEntryArray.push_back({});
1736 }
1737 // Now go through and set up async calls to fill in the entries
1738 size_t index = 0;
1739 for (const std::string &logID : logIDs)
1740 {
1741 // Add the log entry to the array
1742 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07001743 }
1744 asyncResp->res.jsonValue["Members@odata.count"] =
1745 logEntryArray.size();
1746 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001747 crow::connections::systemBus->async_method_call(
1748 std::move(getLogEntriesCallback),
1749 "xyz.openbmc_project.ObjectMapper",
1750 "/xyz/openbmc_project/object_mapper",
1751 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001752 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001753 }
1754};
1755
Jason M. Bills424c4172019-03-21 13:50:33 -07001756class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001757{
1758 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001759 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001760 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001761 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001762 std::string())
1763 {
1764 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001765 {boost::beast::http::verb::get, {{"Login"}}},
1766 {boost::beast::http::verb::head, {{"Login"}}},
1767 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1768 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1769 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1770 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001771 }
1772
1773 private:
1774 void doGet(crow::Response &res, const crow::Request &req,
1775 const std::vector<std::string> &params) override
1776 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001777 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001778 if (params.size() != 1)
1779 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001780 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001781 return;
1782 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001783 const std::string &logID = params[0];
1784 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
1785 }
1786};
1787
1788class CrashdumpFile : public Node
1789{
1790 public:
1791 CrashdumpFile(CrowApp &app) :
1792 Node(app,
1793 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
1794 "<str>/",
1795 std::string(), std::string())
1796 {
1797 entityPrivileges = {
1798 {boost::beast::http::verb::get, {{"Login"}}},
1799 {boost::beast::http::verb::head, {{"Login"}}},
1800 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1801 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1802 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1803 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1804 }
1805
1806 private:
1807 void doGet(crow::Response &res, const crow::Request &req,
1808 const std::vector<std::string> &params) override
1809 {
1810 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1811 if (params.size() != 2)
1812 {
1813 messages::internalError(asyncResp->res);
1814 return;
1815 }
1816 const std::string &logID = params[0];
1817 const std::string &fileName = params[1];
1818
Johnathan Mantey043a0532020-03-10 17:15:28 -07001819 auto getStoredLogCallback =
1820 [asyncResp, logID, fileName](
1821 const boost::system::error_code ec,
1822 const std::vector<std::pair<std::string, VariantType>> &resp) {
1823 if (ec)
1824 {
1825 BMCWEB_LOG_DEBUG << "failed to get log ec: "
1826 << ec.message();
1827 messages::internalError(asyncResp->res);
1828 return;
1829 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001830
Johnathan Mantey043a0532020-03-10 17:15:28 -07001831 std::string dbusFilename{};
1832 std::string dbusTimestamp{};
1833 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07001834
Johnathan Mantey043a0532020-03-10 17:15:28 -07001835 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
1836 dbusFilepath);
1837
1838 if (dbusFilename.empty() || dbusTimestamp.empty() ||
1839 dbusFilepath.empty())
1840 {
1841 messages::resourceMissingAtURI(asyncResp->res, fileName);
1842 return;
1843 }
1844
1845 // Verify the file name parameter is correct
1846 if (fileName != dbusFilename)
1847 {
1848 messages::resourceMissingAtURI(asyncResp->res, fileName);
1849 return;
1850 }
1851
1852 if (!std::filesystem::exists(dbusFilepath))
1853 {
1854 messages::resourceMissingAtURI(asyncResp->res, fileName);
1855 return;
1856 }
1857 std::ifstream ifs(dbusFilepath, std::ios::in |
1858 std::ios::binary |
1859 std::ios::ate);
1860 std::ifstream::pos_type fileSize = ifs.tellg();
1861 if (fileSize < 0)
1862 {
1863 messages::generalError(asyncResp->res);
1864 return;
1865 }
1866 ifs.seekg(0, std::ios::beg);
1867
1868 auto crashData = std::make_unique<char[]>(
1869 static_cast<unsigned int>(fileSize));
1870
1871 ifs.read(crashData.get(), static_cast<int>(fileSize));
1872
1873 // The cast to std::string is intentional in order to use the
1874 // assign() that applies move mechanics
1875 asyncResp->res.body().assign(
1876 static_cast<std::string>(crashData.get()));
1877
1878 // Configure this to be a file download when accessed from
1879 // a browser
1880 asyncResp->res.addHeader("Content-Disposition", "attachment");
1881 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001882 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001883 std::move(getStoredLogCallback), crashdumpObject,
1884 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07001885 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07001886 }
1887};
1888
Jason M. Bills424c4172019-03-21 13:50:33 -07001889class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001890{
1891 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001892 OnDemandCrashdump(CrowApp &app) :
1893 Node(app,
1894 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1895 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001896 {
1897 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001898 {boost::beast::http::verb::get, {{"Login"}}},
1899 {boost::beast::http::verb::head, {{"Login"}}},
1900 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1901 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1902 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1903 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001904 }
1905
1906 private:
1907 void doPost(crow::Response &res, const crow::Request &req,
1908 const std::vector<std::string> &params) override
1909 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001910 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001911
James Feistfe306722020-03-12 16:32:08 -07001912 auto generateonDemandLogCallback = [asyncResp,
1913 req](const boost::system::error_code
1914 ec,
1915 const std::string &resp) {
James Feist46229572020-02-19 15:11:58 -08001916 if (ec)
1917 {
1918 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07001919 {
James Feist46229572020-02-19 15:11:58 -08001920 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001921 }
James Feist46229572020-02-19 15:11:58 -08001922 else if (ec.value() ==
1923 boost::system::errc::device_or_resource_busy)
1924 {
1925 messages::serviceTemporarilyUnavailable(asyncResp->res,
1926 "60");
1927 }
1928 else
1929 {
1930 messages::internalError(asyncResp->res);
1931 }
1932 return;
1933 }
1934 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08001935 [](boost::system::error_code err, sdbusplus::message::message &,
1936 const std::shared_ptr<task::TaskData> &taskData) {
1937 if (!err)
1938 {
1939 taskData->messages.emplace_back(messages::success());
James Feist831d6b02020-03-12 16:31:30 -07001940 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08001941 }
James Feist32898ce2020-03-10 16:16:52 -07001942 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08001943 },
James Feist46229572020-02-19 15:11:58 -08001944 "type='signal',interface='org.freedesktop.DBus.Properties',"
1945 "member='PropertiesChanged',arg0namespace='com.intel."
1946 "crashdump'");
1947 task->startTimer(std::chrono::minutes(5));
1948 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07001949 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08001950 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001951 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001952 std::move(generateonDemandLogCallback), crashdumpObject,
1953 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001954 }
1955};
1956
Jason M. Billse1f26342018-07-18 12:12:00 -07001957class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001958{
1959 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001960 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001961 Node(app,
1962 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1963 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001964 {
1965 entityPrivileges = {
1966 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1967 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1968 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1969 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1970 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1971 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1972 }
1973
1974 private:
1975 void doPost(crow::Response &res, const crow::Request &req,
1976 const std::vector<std::string> &params) override
1977 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001978 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001979 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07001980
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001981 nlohmann::json reqJson =
1982 nlohmann::json::parse(req.body, nullptr, false);
1983 if (reqJson.find("PECICommands") != reqJson.end())
1984 {
1985 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
1986 {
1987 return;
1988 }
1989 uint32_t idx = 0;
1990 for (auto const &cmd : peciCommands)
1991 {
1992 if (cmd.size() < 3)
1993 {
1994 std::string s("[");
1995 for (auto const &val : cmd)
1996 {
1997 if (val != *cmd.begin())
1998 {
1999 s += ",";
2000 }
2001 s += std::to_string(val);
2002 }
2003 s += "]";
2004 messages::actionParameterValueFormatError(
2005 res, s, "PECICommands[" + std::to_string(idx) + "]",
2006 "SendRawPeci");
2007 return;
2008 }
2009 idx++;
2010 }
2011 }
2012 else
2013 {
2014 /* This interface is deprecated */
2015 uint8_t clientAddress = 0;
2016 uint8_t readLength = 0;
2017 std::vector<uint8_t> peciCommand;
2018 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2019 "ReadLength", readLength, "PECICommand",
2020 peciCommand))
2021 {
2022 return;
2023 }
2024 peciCommands.push_back({clientAddress, 0, readLength});
2025 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2026 peciCommand.end());
2027 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002028 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002029 auto sendRawPECICallback =
2030 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002031 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002032 if (ec)
2033 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002034 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002035 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002036 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002037 return;
2038 }
2039 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2040 {"PECIResponse", resp}};
2041 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002042 // Call the SendRawPECI command with the provided data
2043 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002044 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002045 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002046 }
2047};
2048
Andrew Geisslercb92c032018-08-17 07:56:14 -07002049/**
2050 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2051 */
2052class DBusLogServiceActionsClear : public Node
2053{
2054 public:
2055 DBusLogServiceActionsClear(CrowApp &app) :
2056 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Tim Lee1f56a3a2019-10-09 10:17:57 +08002057 "LogService.ClearLog")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002058 {
2059 entityPrivileges = {
2060 {boost::beast::http::verb::get, {{"Login"}}},
2061 {boost::beast::http::verb::head, {{"Login"}}},
2062 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2063 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2064 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2065 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2066 }
2067
2068 private:
2069 /**
2070 * Function handles POST method request.
2071 * The Clear Log actions does not require any parameter.The action deletes
2072 * all entries found in the Entries collection for this Log Service.
2073 */
2074 void doPost(crow::Response &res, const crow::Request &req,
2075 const std::vector<std::string> &params) override
2076 {
2077 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2078
2079 auto asyncResp = std::make_shared<AsyncResp>(res);
2080 // Process response from Logging service.
2081 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2082 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2083 if (ec)
2084 {
2085 // TODO Handle for specific error code
2086 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2087 asyncResp->res.result(
2088 boost::beast::http::status::internal_server_error);
2089 return;
2090 }
2091
2092 asyncResp->res.result(boost::beast::http::status::no_content);
2093 };
2094
2095 // Make call to Logging service to request Clear Log
2096 crow::connections::systemBus->async_method_call(
2097 resp_handler, "xyz.openbmc_project.Logging",
2098 "/xyz/openbmc_project/logging",
2099 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2100 }
2101};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002102
2103/****************************************************
2104 * Redfish PostCode interfaces
2105 * using DBUS interface: getPostCodesTS
2106 ******************************************************/
2107class PostCodesLogService : public Node
2108{
2109 public:
2110 PostCodesLogService(CrowApp &app) :
2111 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2112 {
2113 entityPrivileges = {
2114 {boost::beast::http::verb::get, {{"Login"}}},
2115 {boost::beast::http::verb::head, {{"Login"}}},
2116 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2117 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2118 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2119 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2120 }
2121
2122 private:
2123 void doGet(crow::Response &res, const crow::Request &req,
2124 const std::vector<std::string> &params) override
2125 {
2126 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2127
2128 asyncResp->res.jsonValue = {
2129 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2130 {"@odata.type", "#LogService.v1_1_0.LogService"},
2131 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2132 {"Name", "POST Code Log Service"},
2133 {"Description", "POST Code Log Service"},
2134 {"Id", "BIOS POST Code Log"},
2135 {"OverWritePolicy", "WrapsWhenFull"},
2136 {"Entries",
2137 {{"@odata.id",
2138 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2139 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2140 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2141 "Actions/LogService.ClearLog"}};
2142 }
2143};
2144
2145class PostCodesClear : public Node
2146{
2147 public:
2148 PostCodesClear(CrowApp &app) :
2149 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2150 "LogService.ClearLog/")
2151 {
2152 entityPrivileges = {
2153 {boost::beast::http::verb::get, {{"Login"}}},
2154 {boost::beast::http::verb::head, {{"Login"}}},
2155 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2156 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2157 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2158 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2159 }
2160
2161 private:
2162 void doPost(crow::Response &res, const crow::Request &req,
2163 const std::vector<std::string> &params) override
2164 {
2165 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2166
2167 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2168 // Make call to post-code service to request clear all
2169 crow::connections::systemBus->async_method_call(
2170 [asyncResp](const boost::system::error_code ec) {
2171 if (ec)
2172 {
2173 // TODO Handle for specific error code
2174 BMCWEB_LOG_ERROR
2175 << "doClearPostCodes resp_handler got error " << ec;
2176 asyncResp->res.result(
2177 boost::beast::http::status::internal_server_error);
2178 messages::internalError(asyncResp->res);
2179 return;
2180 }
2181 },
2182 "xyz.openbmc_project.State.Boot.PostCode",
2183 "/xyz/openbmc_project/State/Boot/PostCode",
2184 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2185 }
2186};
2187
2188static void fillPostCodeEntry(
2189 std::shared_ptr<AsyncResp> aResp,
2190 const boost::container::flat_map<uint64_t, uint64_t> &postcode,
2191 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2192 const uint64_t skip = 0, const uint64_t top = 0)
2193{
2194 // Get the Message from the MessageRegistry
2195 const message_registries::Message *message =
2196 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
2197 std::string severity;
2198 if (message != nullptr)
2199 {
2200 severity = message->severity;
2201 }
2202
2203 uint64_t currentCodeIndex = 0;
2204 nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"];
2205
2206 uint64_t firstCodeTimeUs = 0;
2207 for (const std::pair<uint64_t, uint64_t> &code : postcode)
2208 {
2209 currentCodeIndex++;
2210 std::string postcodeEntryID =
2211 "B" + std::to_string(bootIndex) + "-" +
2212 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2213
2214 uint64_t usecSinceEpoch = code.first;
2215 uint64_t usTimeOffset = 0;
2216
2217 if (1 == currentCodeIndex)
2218 { // already incremented
2219 firstCodeTimeUs = code.first;
2220 }
2221 else
2222 {
2223 usTimeOffset = code.first - firstCodeTimeUs;
2224 }
2225
2226 // skip if no specific codeIndex is specified and currentCodeIndex does
2227 // not fall between top and skip
2228 if ((codeIndex == 0) &&
2229 (currentCodeIndex <= skip || currentCodeIndex > top))
2230 {
2231 continue;
2232 }
2233
2234 // skip if a sepcific codeIndex is specified and does not match the
2235 // currentIndex
2236 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2237 {
2238 // This is done for simplicity. 1st entry is needed to calculate
2239 // time offset. To improve efficiency, one can get to the entry
2240 // directly (possibly with flatmap's nth method)
2241 continue;
2242 }
2243
2244 // currentCodeIndex is within top and skip or equal to specified code
2245 // index
2246
2247 // Get the Created time from the timestamp
2248 std::string entryTimeStr;
2249 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2250 {
2251 continue;
2252 }
2253
2254 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2255 std::ostringstream hexCode;
2256 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2257 << code.second;
2258 std::ostringstream timeOffsetStr;
2259 // Set Fixed -Point Notation
2260 timeOffsetStr << std::fixed;
2261 // Set precision to 4 digits
2262 timeOffsetStr << std::setprecision(4);
2263 // Add double to stream
2264 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2265 std::vector<std::string> messageArgs = {
2266 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2267
2268 // Get MessageArgs template from message registry
2269 std::string msg;
2270 if (message != nullptr)
2271 {
2272 msg = message->message;
2273
2274 // fill in this post code value
2275 int i = 0;
2276 for (const std::string &messageArg : messageArgs)
2277 {
2278 std::string argStr = "%" + std::to_string(++i);
2279 size_t argPos = msg.find(argStr);
2280 if (argPos != std::string::npos)
2281 {
2282 msg.replace(argPos, argStr.length(), messageArg);
2283 }
2284 }
2285 }
2286
2287 // add to AsyncResp
2288 logEntryArray.push_back({});
2289 nlohmann::json &bmcLogEntry = logEntryArray.back();
2290 bmcLogEntry = {
2291 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2292 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2293 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2294 "PostCodes/Entries/" +
2295 postcodeEntryID},
2296 {"Name", "POST Code Log Entry"},
2297 {"Id", postcodeEntryID},
2298 {"Message", std::move(msg)},
2299 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2300 {"MessageArgs", std::move(messageArgs)},
2301 {"EntryType", "Event"},
2302 {"Severity", std::move(severity)},
2303 {"Created", std::move(entryTimeStr)}};
2304 }
2305}
2306
2307static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2308 const uint16_t bootIndex,
2309 const uint64_t codeIndex)
2310{
2311 crow::connections::systemBus->async_method_call(
2312 [aResp, bootIndex, codeIndex](
2313 const boost::system::error_code ec,
2314 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2315 if (ec)
2316 {
2317 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2318 messages::internalError(aResp->res);
2319 return;
2320 }
2321
2322 // skip the empty postcode boots
2323 if (postcode.empty())
2324 {
2325 return;
2326 }
2327
2328 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2329
2330 aResp->res.jsonValue["Members@odata.count"] =
2331 aResp->res.jsonValue["Members"].size();
2332 },
2333 "xyz.openbmc_project.State.Boot.PostCode",
2334 "/xyz/openbmc_project/State/Boot/PostCode",
2335 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2336 bootIndex);
2337}
2338
2339static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2340 const uint16_t bootIndex,
2341 const uint16_t bootCount,
2342 const uint64_t entryCount, const uint64_t skip,
2343 const uint64_t top)
2344{
2345 crow::connections::systemBus->async_method_call(
2346 [aResp, bootIndex, bootCount, entryCount, skip,
2347 top](const boost::system::error_code ec,
2348 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2349 if (ec)
2350 {
2351 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2352 messages::internalError(aResp->res);
2353 return;
2354 }
2355
2356 uint64_t endCount = entryCount;
2357 if (!postcode.empty())
2358 {
2359 endCount = entryCount + postcode.size();
2360
2361 if ((skip < endCount) && ((top + skip) > entryCount))
2362 {
2363 uint64_t thisBootSkip =
2364 std::max(skip, entryCount) - entryCount;
2365 uint64_t thisBootTop =
2366 std::min(top + skip, endCount) - entryCount;
2367
2368 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2369 thisBootSkip, thisBootTop);
2370 }
2371 aResp->res.jsonValue["Members@odata.count"] = endCount;
2372 }
2373
2374 // continue to previous bootIndex
2375 if (bootIndex < bootCount)
2376 {
2377 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2378 bootCount, endCount, skip, top);
2379 }
2380 else
2381 {
2382 aResp->res.jsonValue["Members@odata.nextLink"] =
2383 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2384 "Entries?$skip=" +
2385 std::to_string(skip + top);
2386 }
2387 },
2388 "xyz.openbmc_project.State.Boot.PostCode",
2389 "/xyz/openbmc_project/State/Boot/PostCode",
2390 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2391 bootIndex);
2392}
2393
2394static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2395 const uint64_t skip, const uint64_t top)
2396{
2397 uint64_t entryCount = 0;
2398 crow::connections::systemBus->async_method_call(
2399 [aResp, entryCount, skip,
2400 top](const boost::system::error_code ec,
2401 const std::variant<uint16_t> &bootCount) {
2402 if (ec)
2403 {
2404 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2405 messages::internalError(aResp->res);
2406 return;
2407 }
2408 auto pVal = std::get_if<uint16_t>(&bootCount);
2409 if (pVal)
2410 {
2411 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2412 }
2413 else
2414 {
2415 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2416 }
2417 },
2418 "xyz.openbmc_project.State.Boot.PostCode",
2419 "/xyz/openbmc_project/State/Boot/PostCode",
2420 "org.freedesktop.DBus.Properties", "Get",
2421 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2422}
2423
2424class PostCodesEntryCollection : public Node
2425{
2426 public:
2427 template <typename CrowApp>
2428 PostCodesEntryCollection(CrowApp &app) :
2429 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2430 {
2431 entityPrivileges = {
2432 {boost::beast::http::verb::get, {{"Login"}}},
2433 {boost::beast::http::verb::head, {{"Login"}}},
2434 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2435 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2436 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2437 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2438 }
2439
2440 private:
2441 void doGet(crow::Response &res, const crow::Request &req,
2442 const std::vector<std::string> &params) override
2443 {
2444 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2445
2446 asyncResp->res.jsonValue["@odata.type"] =
2447 "#LogEntryCollection.LogEntryCollection";
2448 asyncResp->res.jsonValue["@odata.context"] =
2449 "/redfish/v1/"
2450 "$metadata#LogEntryCollection.LogEntryCollection";
2451 asyncResp->res.jsonValue["@odata.id"] =
2452 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2453 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2454 asyncResp->res.jsonValue["Description"] =
2455 "Collection of POST Code Log Entries";
2456 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2457 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2458
2459 uint64_t skip = 0;
2460 uint64_t top = maxEntriesPerPage; // Show max entries by default
2461 if (!getSkipParam(asyncResp->res, req, skip))
2462 {
2463 return;
2464 }
2465 if (!getTopParam(asyncResp->res, req, top))
2466 {
2467 return;
2468 }
2469 getCurrentBootNumber(asyncResp, skip, top);
2470 }
2471};
2472
2473class PostCodesEntry : public Node
2474{
2475 public:
2476 PostCodesEntry(CrowApp &app) :
2477 Node(app,
2478 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2479 std::string())
2480 {
2481 entityPrivileges = {
2482 {boost::beast::http::verb::get, {{"Login"}}},
2483 {boost::beast::http::verb::head, {{"Login"}}},
2484 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2485 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2486 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2487 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2488 }
2489
2490 private:
2491 void doGet(crow::Response &res, const crow::Request &req,
2492 const std::vector<std::string> &params) override
2493 {
2494 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2495 if (params.size() != 1)
2496 {
2497 messages::internalError(asyncResp->res);
2498 return;
2499 }
2500
2501 const std::string &targetID = params[0];
2502
2503 size_t bootPos = targetID.find('B');
2504 if (bootPos == std::string::npos)
2505 {
2506 // Requested ID was not found
2507 messages::resourceMissingAtURI(asyncResp->res, targetID);
2508 return;
2509 }
2510 std::string_view bootIndexStr(targetID);
2511 bootIndexStr.remove_prefix(bootPos + 1);
2512 uint16_t bootIndex = 0;
2513 uint64_t codeIndex = 0;
2514 size_t dashPos = bootIndexStr.find('-');
2515
2516 if (dashPos == std::string::npos)
2517 {
2518 return;
2519 }
2520 std::string_view codeIndexStr(bootIndexStr);
2521 bootIndexStr.remove_suffix(dashPos);
2522 codeIndexStr.remove_prefix(dashPos + 1);
2523
2524 bootIndex = static_cast<uint16_t>(
2525 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2526 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2527 if (bootIndex == 0 || codeIndex == 0)
2528 {
2529 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2530 << params[0];
2531 }
2532
2533 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2534 asyncResp->res.jsonValue["@odata.context"] =
2535 "/redfish/v1/$metadata#LogEntry.LogEntry";
2536 asyncResp->res.jsonValue["@odata.id"] =
2537 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2538 "Entries";
2539 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2540 asyncResp->res.jsonValue["Description"] =
2541 "Collection of POST Code Log Entries";
2542 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2543 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2544
2545 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2546 }
2547};
2548
Ed Tanous1da66f72018-07-27 16:13:37 -07002549} // namespace redfish