blob: 73a7bb6f4bcdb59fd2ff9ac059696ce9a4a766de [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 {
AppaRao Puli39460282020-04-07 17:03:04 +05301533 // Note: Deviated from redfish privilege registry for GET & HEAD
1534 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001535 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301536 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1537 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001538 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1539 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1540 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1541 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001542 }
1543
1544 private:
1545 /**
1546 * Functions triggers appropriate requests on DBus
1547 */
1548 void doGet(crow::Response &res, const crow::Request &req,
1549 const std::vector<std::string> &params) override
1550 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001551 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001552 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001553 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001554 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001555 asyncResp->res.jsonValue["@odata.type"] =
1556 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001557 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1558 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1559 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001560 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1561 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001562 asyncResp->res.jsonValue["Entries"] = {
1563 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001564 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001565 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001566 {"#LogService.ClearLog",
1567 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1568 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001569 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001570 {{"#Crashdump.OnDemand",
1571 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1572 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001573
1574#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001575 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001576 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001577 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1578 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001579#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001580 }
1581};
1582
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001583class CrashdumpClear : public Node
1584{
1585 public:
1586 CrashdumpClear(CrowApp &app) :
1587 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1588 "LogService.ClearLog/")
1589 {
AppaRao Puli39460282020-04-07 17:03:04 +05301590 // Note: Deviated from redfish privilege registry for GET & HEAD
1591 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001592 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301593 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1594 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001595 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1596 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1597 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1598 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1599 }
1600
1601 private:
1602 void doPost(crow::Response &res, const crow::Request &req,
1603 const std::vector<std::string> &params) override
1604 {
1605 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1606
1607 crow::connections::systemBus->async_method_call(
1608 [asyncResp](const boost::system::error_code ec,
1609 const std::string &resp) {
1610 if (ec)
1611 {
1612 messages::internalError(asyncResp->res);
1613 return;
1614 }
1615 messages::success(asyncResp->res);
1616 },
1617 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
1618 }
1619};
1620
Jason M. Billse855dd22019-10-08 11:37:48 -07001621static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1622 const std::string &logID,
1623 nlohmann::json &logEntryJson)
1624{
Johnathan Mantey043a0532020-03-10 17:15:28 -07001625 auto getStoredLogCallback =
1626 [asyncResp, logID, &logEntryJson](
1627 const boost::system::error_code ec,
1628 const std::vector<std::pair<std::string, VariantType>> &params) {
1629 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001630 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07001631 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1632 if (ec.value() ==
1633 boost::system::linux_error::bad_request_descriptor)
1634 {
1635 messages::resourceNotFound(asyncResp->res, "LogEntry",
1636 logID);
1637 }
1638 else
1639 {
1640 messages::internalError(asyncResp->res);
1641 }
1642 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001643 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001644
Johnathan Mantey043a0532020-03-10 17:15:28 -07001645 std::string timestamp{};
1646 std::string filename{};
1647 std::string logfile{};
1648 ParseCrashdumpParameters(params, filename, timestamp, logfile);
1649
1650 if (filename.empty() || timestamp.empty())
1651 {
1652 messages::resourceMissingAtURI(asyncResp->res, logID);
1653 return;
1654 }
1655
1656 std::string crashdumpURI =
1657 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1658 logID + "/" + filename;
1659 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1660 {"@odata.id", "/redfish/v1/Systems/system/"
1661 "LogServices/Crashdump/Entries/" +
1662 logID},
1663 {"Name", "CPU Crashdump"},
1664 {"Id", logID},
1665 {"EntryType", "Oem"},
1666 {"OemRecordFormat", "Crashdump URI"},
1667 {"Message", std::move(crashdumpURI)},
1668 {"Created", std::move(timestamp)}};
1669 };
Jason M. Billse855dd22019-10-08 11:37:48 -07001670 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001671 std::move(getStoredLogCallback), crashdumpObject,
1672 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07001673 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07001674}
1675
Jason M. Bills424c4172019-03-21 13:50:33 -07001676class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001677{
1678 public:
1679 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001680 CrashdumpEntryCollection(CrowApp &app) :
1681 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001682 {
AppaRao Puli39460282020-04-07 17:03:04 +05301683 // Note: Deviated from redfish privilege registry for GET & HEAD
1684 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001685 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301686 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1687 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001688 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1689 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1690 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1691 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001692 }
1693
1694 private:
1695 /**
1696 * Functions triggers appropriate requests on DBus
1697 */
1698 void doGet(crow::Response &res, const crow::Request &req,
1699 const std::vector<std::string> &params) override
1700 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001701 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001702 // Collections don't include the static data added by SubRoute because
1703 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001704 auto getLogEntriesCallback = [asyncResp](
1705 const boost::system::error_code ec,
1706 const std::vector<std::string> &resp) {
1707 if (ec)
1708 {
1709 if (ec.value() !=
1710 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001711 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001712 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1713 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001714 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001715 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001716 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001717 }
1718 asyncResp->res.jsonValue["@odata.type"] =
1719 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001720 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001721 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07001722 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001723 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001724 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001725 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1726 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07001727 std::vector<std::string> logIDs;
1728 // Get the list of log entries and build up an empty array big
1729 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07001730 for (const std::string &objpath : resp)
1731 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001732 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07001733 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07001734 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07001735 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001736 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07001737 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001738 logIDs.emplace_back(objpath.substr(lastPos + 1));
1739
1740 // Add a space for the log entry to the array
1741 logEntryArray.push_back({});
1742 }
1743 // Now go through and set up async calls to fill in the entries
1744 size_t index = 0;
1745 for (const std::string &logID : logIDs)
1746 {
1747 // Add the log entry to the array
1748 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07001749 }
1750 asyncResp->res.jsonValue["Members@odata.count"] =
1751 logEntryArray.size();
1752 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001753 crow::connections::systemBus->async_method_call(
1754 std::move(getLogEntriesCallback),
1755 "xyz.openbmc_project.ObjectMapper",
1756 "/xyz/openbmc_project/object_mapper",
1757 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001758 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001759 }
1760};
1761
Jason M. Bills424c4172019-03-21 13:50:33 -07001762class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001763{
1764 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001765 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001766 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001767 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001768 std::string())
1769 {
AppaRao Puli39460282020-04-07 17:03:04 +05301770 // Note: Deviated from redfish privilege registry for GET & HEAD
1771 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001772 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301773 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1774 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001775 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1776 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1777 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1778 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001779 }
1780
1781 private:
1782 void doGet(crow::Response &res, const crow::Request &req,
1783 const std::vector<std::string> &params) override
1784 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001785 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001786 if (params.size() != 1)
1787 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001788 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001789 return;
1790 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001791 const std::string &logID = params[0];
1792 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
1793 }
1794};
1795
1796class CrashdumpFile : public Node
1797{
1798 public:
1799 CrashdumpFile(CrowApp &app) :
1800 Node(app,
1801 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
1802 "<str>/",
1803 std::string(), std::string())
1804 {
AppaRao Puli39460282020-04-07 17:03:04 +05301805 // Note: Deviated from redfish privilege registry for GET & HEAD
1806 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07001807 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301808 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1809 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07001810 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1811 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1812 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1813 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1814 }
1815
1816 private:
1817 void doGet(crow::Response &res, const crow::Request &req,
1818 const std::vector<std::string> &params) override
1819 {
1820 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1821 if (params.size() != 2)
1822 {
1823 messages::internalError(asyncResp->res);
1824 return;
1825 }
1826 const std::string &logID = params[0];
1827 const std::string &fileName = params[1];
1828
Johnathan Mantey043a0532020-03-10 17:15:28 -07001829 auto getStoredLogCallback =
1830 [asyncResp, logID, fileName](
1831 const boost::system::error_code ec,
1832 const std::vector<std::pair<std::string, VariantType>> &resp) {
1833 if (ec)
1834 {
1835 BMCWEB_LOG_DEBUG << "failed to get log ec: "
1836 << ec.message();
1837 messages::internalError(asyncResp->res);
1838 return;
1839 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001840
Johnathan Mantey043a0532020-03-10 17:15:28 -07001841 std::string dbusFilename{};
1842 std::string dbusTimestamp{};
1843 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07001844
Johnathan Mantey043a0532020-03-10 17:15:28 -07001845 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
1846 dbusFilepath);
1847
1848 if (dbusFilename.empty() || dbusTimestamp.empty() ||
1849 dbusFilepath.empty())
1850 {
1851 messages::resourceMissingAtURI(asyncResp->res, fileName);
1852 return;
1853 }
1854
1855 // Verify the file name parameter is correct
1856 if (fileName != dbusFilename)
1857 {
1858 messages::resourceMissingAtURI(asyncResp->res, fileName);
1859 return;
1860 }
1861
1862 if (!std::filesystem::exists(dbusFilepath))
1863 {
1864 messages::resourceMissingAtURI(asyncResp->res, fileName);
1865 return;
1866 }
1867 std::ifstream ifs(dbusFilepath, std::ios::in |
1868 std::ios::binary |
1869 std::ios::ate);
1870 std::ifstream::pos_type fileSize = ifs.tellg();
1871 if (fileSize < 0)
1872 {
1873 messages::generalError(asyncResp->res);
1874 return;
1875 }
1876 ifs.seekg(0, std::ios::beg);
1877
1878 auto crashData = std::make_unique<char[]>(
1879 static_cast<unsigned int>(fileSize));
1880
1881 ifs.read(crashData.get(), static_cast<int>(fileSize));
1882
1883 // The cast to std::string is intentional in order to use the
1884 // assign() that applies move mechanics
1885 asyncResp->res.body().assign(
1886 static_cast<std::string>(crashData.get()));
1887
1888 // Configure this to be a file download when accessed from
1889 // a browser
1890 asyncResp->res.addHeader("Content-Disposition", "attachment");
1891 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001892 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001893 std::move(getStoredLogCallback), crashdumpObject,
1894 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07001895 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07001896 }
1897};
1898
Jason M. Bills424c4172019-03-21 13:50:33 -07001899class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001900{
1901 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001902 OnDemandCrashdump(CrowApp &app) :
1903 Node(app,
1904 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1905 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001906 {
AppaRao Puli39460282020-04-07 17:03:04 +05301907 // Note: Deviated from redfish privilege registry for GET & HEAD
1908 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001909 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301910 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1911 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1912 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1913 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1914 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1915 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001916 }
1917
1918 private:
1919 void doPost(crow::Response &res, const crow::Request &req,
1920 const std::vector<std::string> &params) override
1921 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001922 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001923
James Feistfe306722020-03-12 16:32:08 -07001924 auto generateonDemandLogCallback = [asyncResp,
1925 req](const boost::system::error_code
1926 ec,
1927 const std::string &resp) {
James Feist46229572020-02-19 15:11:58 -08001928 if (ec)
1929 {
1930 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07001931 {
James Feist46229572020-02-19 15:11:58 -08001932 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001933 }
James Feist46229572020-02-19 15:11:58 -08001934 else if (ec.value() ==
1935 boost::system::errc::device_or_resource_busy)
1936 {
1937 messages::serviceTemporarilyUnavailable(asyncResp->res,
1938 "60");
1939 }
1940 else
1941 {
1942 messages::internalError(asyncResp->res);
1943 }
1944 return;
1945 }
1946 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08001947 [](boost::system::error_code err, sdbusplus::message::message &,
1948 const std::shared_ptr<task::TaskData> &taskData) {
1949 if (!err)
1950 {
1951 taskData->messages.emplace_back(messages::success());
James Feist831d6b02020-03-12 16:31:30 -07001952 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08001953 }
James Feist32898ce2020-03-10 16:16:52 -07001954 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08001955 },
James Feist46229572020-02-19 15:11:58 -08001956 "type='signal',interface='org.freedesktop.DBus.Properties',"
1957 "member='PropertiesChanged',arg0namespace='com.intel."
1958 "crashdump'");
1959 task->startTimer(std::chrono::minutes(5));
1960 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07001961 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08001962 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001963 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001964 std::move(generateonDemandLogCallback), crashdumpObject,
1965 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001966 }
1967};
1968
Jason M. Billse1f26342018-07-18 12:12:00 -07001969class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001970{
1971 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001972 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001973 Node(app,
1974 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1975 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001976 {
AppaRao Puli39460282020-04-07 17:03:04 +05301977 // Note: Deviated from redfish privilege registry for GET & HEAD
1978 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001979 entityPrivileges = {
1980 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1981 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1982 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1983 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1984 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1985 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1986 }
1987
1988 private:
1989 void doPost(crow::Response &res, const crow::Request &req,
1990 const std::vector<std::string> &params) override
1991 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001992 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001993 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07001994
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001995 nlohmann::json reqJson =
1996 nlohmann::json::parse(req.body, nullptr, false);
1997 if (reqJson.find("PECICommands") != reqJson.end())
1998 {
1999 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2000 {
2001 return;
2002 }
2003 uint32_t idx = 0;
2004 for (auto const &cmd : peciCommands)
2005 {
2006 if (cmd.size() < 3)
2007 {
2008 std::string s("[");
2009 for (auto const &val : cmd)
2010 {
2011 if (val != *cmd.begin())
2012 {
2013 s += ",";
2014 }
2015 s += std::to_string(val);
2016 }
2017 s += "]";
2018 messages::actionParameterValueFormatError(
2019 res, s, "PECICommands[" + std::to_string(idx) + "]",
2020 "SendRawPeci");
2021 return;
2022 }
2023 idx++;
2024 }
2025 }
2026 else
2027 {
2028 /* This interface is deprecated */
2029 uint8_t clientAddress = 0;
2030 uint8_t readLength = 0;
2031 std::vector<uint8_t> peciCommand;
2032 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2033 "ReadLength", readLength, "PECICommand",
2034 peciCommand))
2035 {
2036 return;
2037 }
2038 peciCommands.push_back({clientAddress, 0, readLength});
2039 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2040 peciCommand.end());
2041 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002042 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002043 auto sendRawPECICallback =
2044 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002045 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002046 if (ec)
2047 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002048 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002049 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002050 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002051 return;
2052 }
2053 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2054 {"PECIResponse", resp}};
2055 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002056 // Call the SendRawPECI command with the provided data
2057 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002058 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002059 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002060 }
2061};
2062
Andrew Geisslercb92c032018-08-17 07:56:14 -07002063/**
2064 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2065 */
2066class DBusLogServiceActionsClear : public Node
2067{
2068 public:
2069 DBusLogServiceActionsClear(CrowApp &app) :
2070 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002071 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002072 {
2073 entityPrivileges = {
2074 {boost::beast::http::verb::get, {{"Login"}}},
2075 {boost::beast::http::verb::head, {{"Login"}}},
2076 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2077 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2078 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2079 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2080 }
2081
2082 private:
2083 /**
2084 * Function handles POST method request.
2085 * The Clear Log actions does not require any parameter.The action deletes
2086 * all entries found in the Entries collection for this Log Service.
2087 */
2088 void doPost(crow::Response &res, const crow::Request &req,
2089 const std::vector<std::string> &params) override
2090 {
2091 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2092
2093 auto asyncResp = std::make_shared<AsyncResp>(res);
2094 // Process response from Logging service.
2095 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2096 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2097 if (ec)
2098 {
2099 // TODO Handle for specific error code
2100 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2101 asyncResp->res.result(
2102 boost::beast::http::status::internal_server_error);
2103 return;
2104 }
2105
2106 asyncResp->res.result(boost::beast::http::status::no_content);
2107 };
2108
2109 // Make call to Logging service to request Clear Log
2110 crow::connections::systemBus->async_method_call(
2111 resp_handler, "xyz.openbmc_project.Logging",
2112 "/xyz/openbmc_project/logging",
2113 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2114 }
2115};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002116
2117/****************************************************
2118 * Redfish PostCode interfaces
2119 * using DBUS interface: getPostCodesTS
2120 ******************************************************/
2121class PostCodesLogService : public Node
2122{
2123 public:
2124 PostCodesLogService(CrowApp &app) :
2125 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2126 {
2127 entityPrivileges = {
2128 {boost::beast::http::verb::get, {{"Login"}}},
2129 {boost::beast::http::verb::head, {{"Login"}}},
2130 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2131 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2132 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2133 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2134 }
2135
2136 private:
2137 void doGet(crow::Response &res, const crow::Request &req,
2138 const std::vector<std::string> &params) override
2139 {
2140 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2141
2142 asyncResp->res.jsonValue = {
2143 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2144 {"@odata.type", "#LogService.v1_1_0.LogService"},
2145 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2146 {"Name", "POST Code Log Service"},
2147 {"Description", "POST Code Log Service"},
2148 {"Id", "BIOS POST Code Log"},
2149 {"OverWritePolicy", "WrapsWhenFull"},
2150 {"Entries",
2151 {{"@odata.id",
2152 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2153 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2154 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2155 "Actions/LogService.ClearLog"}};
2156 }
2157};
2158
2159class PostCodesClear : public Node
2160{
2161 public:
2162 PostCodesClear(CrowApp &app) :
2163 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2164 "LogService.ClearLog/")
2165 {
2166 entityPrivileges = {
2167 {boost::beast::http::verb::get, {{"Login"}}},
2168 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302169 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2170 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2171 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2172 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002173 }
2174
2175 private:
2176 void doPost(crow::Response &res, const crow::Request &req,
2177 const std::vector<std::string> &params) override
2178 {
2179 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2180
2181 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2182 // Make call to post-code service to request clear all
2183 crow::connections::systemBus->async_method_call(
2184 [asyncResp](const boost::system::error_code ec) {
2185 if (ec)
2186 {
2187 // TODO Handle for specific error code
2188 BMCWEB_LOG_ERROR
2189 << "doClearPostCodes resp_handler got error " << ec;
2190 asyncResp->res.result(
2191 boost::beast::http::status::internal_server_error);
2192 messages::internalError(asyncResp->res);
2193 return;
2194 }
2195 },
2196 "xyz.openbmc_project.State.Boot.PostCode",
2197 "/xyz/openbmc_project/State/Boot/PostCode",
2198 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2199 }
2200};
2201
2202static void fillPostCodeEntry(
2203 std::shared_ptr<AsyncResp> aResp,
2204 const boost::container::flat_map<uint64_t, uint64_t> &postcode,
2205 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2206 const uint64_t skip = 0, const uint64_t top = 0)
2207{
2208 // Get the Message from the MessageRegistry
2209 const message_registries::Message *message =
2210 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
2211 std::string severity;
2212 if (message != nullptr)
2213 {
2214 severity = message->severity;
2215 }
2216
2217 uint64_t currentCodeIndex = 0;
2218 nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"];
2219
2220 uint64_t firstCodeTimeUs = 0;
2221 for (const std::pair<uint64_t, uint64_t> &code : postcode)
2222 {
2223 currentCodeIndex++;
2224 std::string postcodeEntryID =
2225 "B" + std::to_string(bootIndex) + "-" +
2226 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2227
2228 uint64_t usecSinceEpoch = code.first;
2229 uint64_t usTimeOffset = 0;
2230
2231 if (1 == currentCodeIndex)
2232 { // already incremented
2233 firstCodeTimeUs = code.first;
2234 }
2235 else
2236 {
2237 usTimeOffset = code.first - firstCodeTimeUs;
2238 }
2239
2240 // skip if no specific codeIndex is specified and currentCodeIndex does
2241 // not fall between top and skip
2242 if ((codeIndex == 0) &&
2243 (currentCodeIndex <= skip || currentCodeIndex > top))
2244 {
2245 continue;
2246 }
2247
2248 // skip if a sepcific codeIndex is specified and does not match the
2249 // currentIndex
2250 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2251 {
2252 // This is done for simplicity. 1st entry is needed to calculate
2253 // time offset. To improve efficiency, one can get to the entry
2254 // directly (possibly with flatmap's nth method)
2255 continue;
2256 }
2257
2258 // currentCodeIndex is within top and skip or equal to specified code
2259 // index
2260
2261 // Get the Created time from the timestamp
2262 std::string entryTimeStr;
2263 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2264 {
2265 continue;
2266 }
2267
2268 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2269 std::ostringstream hexCode;
2270 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2271 << code.second;
2272 std::ostringstream timeOffsetStr;
2273 // Set Fixed -Point Notation
2274 timeOffsetStr << std::fixed;
2275 // Set precision to 4 digits
2276 timeOffsetStr << std::setprecision(4);
2277 // Add double to stream
2278 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2279 std::vector<std::string> messageArgs = {
2280 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2281
2282 // Get MessageArgs template from message registry
2283 std::string msg;
2284 if (message != nullptr)
2285 {
2286 msg = message->message;
2287
2288 // fill in this post code value
2289 int i = 0;
2290 for (const std::string &messageArg : messageArgs)
2291 {
2292 std::string argStr = "%" + std::to_string(++i);
2293 size_t argPos = msg.find(argStr);
2294 if (argPos != std::string::npos)
2295 {
2296 msg.replace(argPos, argStr.length(), messageArg);
2297 }
2298 }
2299 }
2300
2301 // add to AsyncResp
2302 logEntryArray.push_back({});
2303 nlohmann::json &bmcLogEntry = logEntryArray.back();
2304 bmcLogEntry = {
2305 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2306 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2307 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2308 "PostCodes/Entries/" +
2309 postcodeEntryID},
2310 {"Name", "POST Code Log Entry"},
2311 {"Id", postcodeEntryID},
2312 {"Message", std::move(msg)},
2313 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2314 {"MessageArgs", std::move(messageArgs)},
2315 {"EntryType", "Event"},
2316 {"Severity", std::move(severity)},
2317 {"Created", std::move(entryTimeStr)}};
2318 }
2319}
2320
2321static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2322 const uint16_t bootIndex,
2323 const uint64_t codeIndex)
2324{
2325 crow::connections::systemBus->async_method_call(
2326 [aResp, bootIndex, codeIndex](
2327 const boost::system::error_code ec,
2328 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2329 if (ec)
2330 {
2331 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2332 messages::internalError(aResp->res);
2333 return;
2334 }
2335
2336 // skip the empty postcode boots
2337 if (postcode.empty())
2338 {
2339 return;
2340 }
2341
2342 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2343
2344 aResp->res.jsonValue["Members@odata.count"] =
2345 aResp->res.jsonValue["Members"].size();
2346 },
2347 "xyz.openbmc_project.State.Boot.PostCode",
2348 "/xyz/openbmc_project/State/Boot/PostCode",
2349 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2350 bootIndex);
2351}
2352
2353static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2354 const uint16_t bootIndex,
2355 const uint16_t bootCount,
2356 const uint64_t entryCount, const uint64_t skip,
2357 const uint64_t top)
2358{
2359 crow::connections::systemBus->async_method_call(
2360 [aResp, bootIndex, bootCount, entryCount, skip,
2361 top](const boost::system::error_code ec,
2362 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2363 if (ec)
2364 {
2365 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2366 messages::internalError(aResp->res);
2367 return;
2368 }
2369
2370 uint64_t endCount = entryCount;
2371 if (!postcode.empty())
2372 {
2373 endCount = entryCount + postcode.size();
2374
2375 if ((skip < endCount) && ((top + skip) > entryCount))
2376 {
2377 uint64_t thisBootSkip =
2378 std::max(skip, entryCount) - entryCount;
2379 uint64_t thisBootTop =
2380 std::min(top + skip, endCount) - entryCount;
2381
2382 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2383 thisBootSkip, thisBootTop);
2384 }
2385 aResp->res.jsonValue["Members@odata.count"] = endCount;
2386 }
2387
2388 // continue to previous bootIndex
2389 if (bootIndex < bootCount)
2390 {
2391 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2392 bootCount, endCount, skip, top);
2393 }
2394 else
2395 {
2396 aResp->res.jsonValue["Members@odata.nextLink"] =
2397 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2398 "Entries?$skip=" +
2399 std::to_string(skip + top);
2400 }
2401 },
2402 "xyz.openbmc_project.State.Boot.PostCode",
2403 "/xyz/openbmc_project/State/Boot/PostCode",
2404 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2405 bootIndex);
2406}
2407
2408static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2409 const uint64_t skip, const uint64_t top)
2410{
2411 uint64_t entryCount = 0;
2412 crow::connections::systemBus->async_method_call(
2413 [aResp, entryCount, skip,
2414 top](const boost::system::error_code ec,
2415 const std::variant<uint16_t> &bootCount) {
2416 if (ec)
2417 {
2418 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2419 messages::internalError(aResp->res);
2420 return;
2421 }
2422 auto pVal = std::get_if<uint16_t>(&bootCount);
2423 if (pVal)
2424 {
2425 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2426 }
2427 else
2428 {
2429 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2430 }
2431 },
2432 "xyz.openbmc_project.State.Boot.PostCode",
2433 "/xyz/openbmc_project/State/Boot/PostCode",
2434 "org.freedesktop.DBus.Properties", "Get",
2435 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2436}
2437
2438class PostCodesEntryCollection : public Node
2439{
2440 public:
2441 template <typename CrowApp>
2442 PostCodesEntryCollection(CrowApp &app) :
2443 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2444 {
2445 entityPrivileges = {
2446 {boost::beast::http::verb::get, {{"Login"}}},
2447 {boost::beast::http::verb::head, {{"Login"}}},
2448 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2449 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2450 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2451 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2452 }
2453
2454 private:
2455 void doGet(crow::Response &res, const crow::Request &req,
2456 const std::vector<std::string> &params) override
2457 {
2458 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2459
2460 asyncResp->res.jsonValue["@odata.type"] =
2461 "#LogEntryCollection.LogEntryCollection";
2462 asyncResp->res.jsonValue["@odata.context"] =
2463 "/redfish/v1/"
2464 "$metadata#LogEntryCollection.LogEntryCollection";
2465 asyncResp->res.jsonValue["@odata.id"] =
2466 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2467 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2468 asyncResp->res.jsonValue["Description"] =
2469 "Collection of POST Code Log Entries";
2470 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2471 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2472
2473 uint64_t skip = 0;
2474 uint64_t top = maxEntriesPerPage; // Show max entries by default
2475 if (!getSkipParam(asyncResp->res, req, skip))
2476 {
2477 return;
2478 }
2479 if (!getTopParam(asyncResp->res, req, top))
2480 {
2481 return;
2482 }
2483 getCurrentBootNumber(asyncResp, skip, top);
2484 }
2485};
2486
2487class PostCodesEntry : public Node
2488{
2489 public:
2490 PostCodesEntry(CrowApp &app) :
2491 Node(app,
2492 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2493 std::string())
2494 {
2495 entityPrivileges = {
2496 {boost::beast::http::verb::get, {{"Login"}}},
2497 {boost::beast::http::verb::head, {{"Login"}}},
2498 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2499 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2500 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2501 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2502 }
2503
2504 private:
2505 void doGet(crow::Response &res, const crow::Request &req,
2506 const std::vector<std::string> &params) override
2507 {
2508 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2509 if (params.size() != 1)
2510 {
2511 messages::internalError(asyncResp->res);
2512 return;
2513 }
2514
2515 const std::string &targetID = params[0];
2516
2517 size_t bootPos = targetID.find('B');
2518 if (bootPos == std::string::npos)
2519 {
2520 // Requested ID was not found
2521 messages::resourceMissingAtURI(asyncResp->res, targetID);
2522 return;
2523 }
2524 std::string_view bootIndexStr(targetID);
2525 bootIndexStr.remove_prefix(bootPos + 1);
2526 uint16_t bootIndex = 0;
2527 uint64_t codeIndex = 0;
2528 size_t dashPos = bootIndexStr.find('-');
2529
2530 if (dashPos == std::string::npos)
2531 {
2532 return;
2533 }
2534 std::string_view codeIndexStr(bootIndexStr);
2535 bootIndexStr.remove_suffix(dashPos);
2536 codeIndexStr.remove_prefix(dashPos + 1);
2537
2538 bootIndex = static_cast<uint16_t>(
2539 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2540 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2541 if (bootIndex == 0 || codeIndex == 0)
2542 {
2543 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2544 << params[0];
2545 }
2546
2547 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2548 asyncResp->res.jsonValue["@odata.context"] =
2549 "/redfish/v1/$metadata#LogEntry.LogEntry";
2550 asyncResp->res.jsonValue["@odata.id"] =
2551 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2552 "Entries";
2553 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2554 asyncResp->res.jsonValue["Description"] =
2555 "Collection of POST Code Log Entries";
2556 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2557 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2558
2559 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2560 }
2561};
2562
Ed Tanous1da66f72018-07-27 16:13:37 -07002563} // namespace redfish