blob: f8640076f2d1191c13777263302008b31b1ababc [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
ZhikuiRena3316fc2020-01-29 14:58:08 -0800429constexpr char const *postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800430class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700431{
432 public:
433 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800434 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800435 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800436 {
437 entityPrivileges = {
438 {boost::beast::http::verb::get, {{"Login"}}},
439 {boost::beast::http::verb::head, {{"Login"}}},
440 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
441 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
442 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
443 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
444 }
445
446 private:
447 /**
448 * Functions triggers appropriate requests on DBus
449 */
450 void doGet(crow::Response &res, const crow::Request &req,
451 const std::vector<std::string> &params) override
452 {
453 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800454 // Collections don't include the static data added by SubRoute because
455 // it has a duplicate entry for members
456 asyncResp->res.jsonValue["@odata.type"] =
457 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800458 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800459 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800460 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
461 asyncResp->res.jsonValue["Description"] =
462 "Collection of LogServices for this Computer System";
463 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
464 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800465 logServiceArray.push_back(
466 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800467#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
468 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500469 {{"@odata.id",
470 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800471#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800472 asyncResp->res.jsonValue["Members@odata.count"] =
473 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800474
475 crow::connections::systemBus->async_method_call(
476 [asyncResp](const boost::system::error_code ec,
477 const std::vector<std::string> &subtreePath) {
478 if (ec)
479 {
480 BMCWEB_LOG_ERROR << ec;
481 return;
482 }
483
484 for (auto &pathStr : subtreePath)
485 {
486 if (pathStr.find("PostCode") != std::string::npos)
487 {
488 nlohmann::json &logServiceArray =
489 asyncResp->res.jsonValue["Members"];
490 logServiceArray.push_back(
491 {{"@odata.id", "/redfish/v1/Systems/system/"
492 "LogServices/PostCodes"}});
493 asyncResp->res.jsonValue["Members@odata.count"] =
494 logServiceArray.size();
495 return;
496 }
497 }
498 },
499 "xyz.openbmc_project.ObjectMapper",
500 "/xyz/openbmc_project/object_mapper",
501 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
502 std::array<const char *, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800503 }
504};
505
506class EventLogService : public Node
507{
508 public:
509 template <typename CrowApp>
510 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800511 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800512 {
513 entityPrivileges = {
514 {boost::beast::http::verb::get, {{"Login"}}},
515 {boost::beast::http::verb::head, {{"Login"}}},
516 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
517 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
518 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
519 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
520 }
521
522 private:
523 void doGet(crow::Response &res, const crow::Request &req,
524 const std::vector<std::string> &params) override
525 {
526 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
527
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800528 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800529 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800530 asyncResp->res.jsonValue["@odata.type"] =
531 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800532 asyncResp->res.jsonValue["Name"] = "Event Log Service";
533 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
534 asyncResp->res.jsonValue["Id"] = "Event Log";
535 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
536 asyncResp->res.jsonValue["Entries"] = {
537 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800538 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500539 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
540
541 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
542 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700543 }
544};
545
Tim Lee1f56a3a2019-10-09 10:17:57 +0800546class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700547{
548 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800549 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700550 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
551 "LogService.ClearLog/")
552 {
553 entityPrivileges = {
554 {boost::beast::http::verb::get, {{"Login"}}},
555 {boost::beast::http::verb::head, {{"Login"}}},
556 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
557 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
558 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
559 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
560 }
561
562 private:
563 void doPost(crow::Response &res, const crow::Request &req,
564 const std::vector<std::string> &params) override
565 {
566 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
567
568 // Clear the EventLog by deleting the log files
569 std::vector<std::filesystem::path> redfishLogFiles;
570 if (getRedfishLogFiles(redfishLogFiles))
571 {
572 for (const std::filesystem::path &file : redfishLogFiles)
573 {
574 std::error_code ec;
575 std::filesystem::remove(file, ec);
576 }
577 }
578
579 // Reload rsyslog so it knows to start new log files
580 crow::connections::systemBus->async_method_call(
581 [asyncResp](const boost::system::error_code ec) {
582 if (ec)
583 {
584 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
585 messages::internalError(asyncResp->res);
586 return;
587 }
588
589 messages::success(asyncResp->res);
590 },
591 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
592 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
593 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800594 }
595};
596
Jason M. Bills95820182019-04-22 16:25:34 -0700597static int fillEventLogEntryJson(const std::string &logEntryID,
598 const std::string logEntry,
599 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800600{
Jason M. Bills95820182019-04-22 16:25:34 -0700601 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700602 // First get the Timestamp
603 size_t space = logEntry.find_first_of(" ");
604 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700605 {
606 return 1;
607 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700608 std::string timestamp = logEntry.substr(0, space);
609 // Then get the log contents
610 size_t entryStart = logEntry.find_first_not_of(" ", space);
611 if (entryStart == std::string::npos)
612 {
613 return 1;
614 }
615 std::string_view entry(logEntry);
616 entry.remove_prefix(entryStart);
617 // Use split to separate the entry into its fields
618 std::vector<std::string> logEntryFields;
619 boost::split(logEntryFields, entry, boost::is_any_of(","),
620 boost::token_compress_on);
621 // We need at least a MessageId to be valid
622 if (logEntryFields.size() < 1)
623 {
624 return 1;
625 }
626 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700627
Jason M. Bills4851d452019-03-28 11:27:48 -0700628 // Get the Message from the MessageRegistry
629 const message_registries::Message *message =
630 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800631
Jason M. Bills4851d452019-03-28 11:27:48 -0700632 std::string msg;
633 std::string severity;
634 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800635 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700636 msg = message->message;
637 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800638 }
639
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700640 // Get the MessageArgs from the log if there are any
641 boost::beast::span<std::string> messageArgs;
642 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700643 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700644 std::string &messageArgsStart = logEntryFields[1];
645 // If the first string is empty, assume there are no MessageArgs
646 std::size_t messageArgsSize = 0;
647 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700648 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700649 messageArgsSize = logEntryFields.size() - 1;
650 }
651
652 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
653
654 // Fill the MessageArgs into the Message
655 int i = 0;
656 for (const std::string &messageArg : messageArgs)
657 {
658 std::string argStr = "%" + std::to_string(++i);
659 size_t argPos = msg.find(argStr);
660 if (argPos != std::string::npos)
661 {
662 msg.replace(argPos, argStr.length(), messageArg);
663 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700664 }
665 }
666
Jason M. Bills95820182019-04-22 16:25:34 -0700667 // Get the Created time from the timestamp. The log timestamp is in RFC3339
668 // format which matches the Redfish format except for the fractional seconds
669 // between the '.' and the '+', so just remove them.
670 std::size_t dot = timestamp.find_first_of(".");
671 std::size_t plus = timestamp.find_first_of("+");
672 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800673 {
Jason M. Bills95820182019-04-22 16:25:34 -0700674 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800675 }
676
677 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700678 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700679 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800680 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700681 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700682 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800683 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700684 {"Id", logEntryID},
685 {"Message", std::move(msg)},
686 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800687 {"MessageArgs", std::move(messageArgs)},
688 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700689 {"Severity", std::move(severity)},
690 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800691 return 0;
692}
693
Anthony Wilson27062602019-04-22 02:10:09 -0500694class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800695{
696 public:
697 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500698 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800699 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800700 {
701 entityPrivileges = {
702 {boost::beast::http::verb::get, {{"Login"}}},
703 {boost::beast::http::verb::head, {{"Login"}}},
704 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
705 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
706 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
707 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
708 }
709
710 private:
711 void doGet(crow::Response &res, const crow::Request &req,
712 const std::vector<std::string> &params) override
713 {
714 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700715 uint64_t skip = 0;
716 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800717 if (!getSkipParam(asyncResp->res, req, skip))
718 {
719 return;
720 }
721 if (!getTopParam(asyncResp->res, req, top))
722 {
723 return;
724 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800725 // Collections don't include the static data added by SubRoute because
726 // it has a duplicate entry for members
727 asyncResp->res.jsonValue["@odata.type"] =
728 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800729 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800730 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800731 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
732 asyncResp->res.jsonValue["Description"] =
733 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700734
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800735 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
736 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700737 // Go through the log files and create a unique ID for each entry
738 std::vector<std::filesystem::path> redfishLogFiles;
739 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000740 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700741 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700742
743 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700744 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
745 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800746 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700747 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700748 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800749 {
750 continue;
751 }
752
Jason M. Billse85d6b12019-07-29 17:01:15 -0700753 // Reset the unique ID on the first entry
754 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700755 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800756 {
Jason M. Bills95820182019-04-22 16:25:34 -0700757 entryCount++;
758 // Handle paging using skip (number of entries to skip from the
759 // start) and top (number of entries to display)
760 if (entryCount <= skip || entryCount > skip + top)
761 {
762 continue;
763 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800764
Jason M. Bills95820182019-04-22 16:25:34 -0700765 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700766 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700767 {
768 continue;
769 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800770
Jason M. Billse85d6b12019-07-29 17:01:15 -0700771 if (firstEntry)
772 {
773 firstEntry = false;
774 }
775
Jason M. Bills95820182019-04-22 16:25:34 -0700776 logEntryArray.push_back({});
777 nlohmann::json &bmcLogEntry = logEntryArray.back();
778 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
779 {
780 messages::internalError(asyncResp->res);
781 return;
782 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800783 }
784 }
785 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
786 if (skip + top < entryCount)
787 {
788 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700789 "/redfish/v1/Systems/system/LogServices/EventLog/"
790 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800791 std::to_string(skip + top);
792 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500793 }
794};
795
Jason M. Bills897967d2019-07-29 17:05:30 -0700796class JournalEventLogEntry : public Node
797{
798 public:
799 JournalEventLogEntry(CrowApp &app) :
800 Node(app,
801 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
802 std::string())
803 {
804 entityPrivileges = {
805 {boost::beast::http::verb::get, {{"Login"}}},
806 {boost::beast::http::verb::head, {{"Login"}}},
807 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
808 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
809 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
810 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
811 }
812
813 private:
814 void doGet(crow::Response &res, const crow::Request &req,
815 const std::vector<std::string> &params) override
816 {
817 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
818 if (params.size() != 1)
819 {
820 messages::internalError(asyncResp->res);
821 return;
822 }
823 const std::string &targetID = params[0];
824
825 // Go through the log files and check the unique ID for each entry to
826 // find the target entry
827 std::vector<std::filesystem::path> redfishLogFiles;
828 getRedfishLogFiles(redfishLogFiles);
829 std::string logEntry;
830
831 // Oldest logs are in the last file, so start there and loop backwards
832 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
833 it++)
834 {
835 std::ifstream logStream(*it);
836 if (!logStream.is_open())
837 {
838 continue;
839 }
840
841 // Reset the unique ID on the first entry
842 bool firstEntry = true;
843 while (std::getline(logStream, logEntry))
844 {
845 std::string idStr;
846 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
847 {
848 continue;
849 }
850
851 if (firstEntry)
852 {
853 firstEntry = false;
854 }
855
856 if (idStr == targetID)
857 {
858 if (fillEventLogEntryJson(idStr, logEntry,
859 asyncResp->res.jsonValue) != 0)
860 {
861 messages::internalError(asyncResp->res);
862 return;
863 }
864 return;
865 }
866 }
867 }
868 // Requested ID was not found
869 messages::resourceMissingAtURI(asyncResp->res, targetID);
870 }
871};
872
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500873class DBusEventLogEntryCollection : public Node
874{
875 public:
876 template <typename CrowApp>
877 DBusEventLogEntryCollection(CrowApp &app) :
878 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
879 {
880 entityPrivileges = {
881 {boost::beast::http::verb::get, {{"Login"}}},
882 {boost::beast::http::verb::head, {{"Login"}}},
883 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
884 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
885 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
886 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
887 }
888
889 private:
890 void doGet(crow::Response &res, const crow::Request &req,
891 const std::vector<std::string> &params) override
892 {
893 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
894
895 // Collections don't include the static data added by SubRoute because
896 // it has a duplicate entry for members
897 asyncResp->res.jsonValue["@odata.type"] =
898 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500899 asyncResp->res.jsonValue["@odata.id"] =
900 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
901 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
902 asyncResp->res.jsonValue["Description"] =
903 "Collection of System Event Log Entries";
904
Andrew Geisslercb92c032018-08-17 07:56:14 -0700905 // DBus implementation of EventLog/Entries
906 // Make call to Logging Service to find all log entry objects
907 crow::connections::systemBus->async_method_call(
908 [asyncResp](const boost::system::error_code ec,
909 GetManagedObjectsType &resp) {
910 if (ec)
911 {
912 // TODO Handle for specific error code
913 BMCWEB_LOG_ERROR
914 << "getLogEntriesIfaceData resp_handler got error "
915 << ec;
916 messages::internalError(asyncResp->res);
917 return;
918 }
919 nlohmann::json &entriesArray =
920 asyncResp->res.jsonValue["Members"];
921 entriesArray = nlohmann::json::array();
922 for (auto &objectPath : resp)
923 {
924 for (auto &interfaceMap : objectPath.second)
925 {
926 if (interfaceMap.first !=
927 "xyz.openbmc_project.Logging.Entry")
928 {
929 BMCWEB_LOG_DEBUG << "Bailing early on "
930 << interfaceMap.first;
931 continue;
932 }
933 entriesArray.push_back({});
934 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -0700935 uint32_t *id = nullptr;
936 std::time_t timestamp{};
937 std::string *severity = nullptr;
938 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700939 for (auto &propertyMap : interfaceMap.second)
940 {
941 if (propertyMap.first == "Id")
942 {
943 id = sdbusplus::message::variant_ns::get_if<
944 uint32_t>(&propertyMap.second);
945 if (id == nullptr)
946 {
947 messages::propertyMissing(asyncResp->res,
948 "Id");
949 }
950 }
951 else if (propertyMap.first == "Timestamp")
952 {
953 const uint64_t *millisTimeStamp =
954 std::get_if<uint64_t>(&propertyMap.second);
955 if (millisTimeStamp == nullptr)
956 {
957 messages::propertyMissing(asyncResp->res,
958 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -0700959 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700960 }
961 // Retrieve Created property with format:
962 // yyyy-mm-ddThh:mm:ss
963 std::chrono::milliseconds chronoTimeStamp(
964 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -0700965 timestamp = std::chrono::duration_cast<
966 std::chrono::duration<int>>(
967 chronoTimeStamp)
968 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -0700969 }
970 else if (propertyMap.first == "Severity")
971 {
972 severity = std::get_if<std::string>(
973 &propertyMap.second);
974 if (severity == nullptr)
975 {
976 messages::propertyMissing(asyncResp->res,
977 "Severity");
978 }
979 }
980 else if (propertyMap.first == "Message")
981 {
982 message = std::get_if<std::string>(
983 &propertyMap.second);
984 if (message == nullptr)
985 {
986 messages::propertyMissing(asyncResp->res,
987 "Message");
988 }
989 }
990 }
991 thisEntry = {
992 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700993 {"@odata.id",
994 "/redfish/v1/Systems/system/LogServices/EventLog/"
995 "Entries/" +
996 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -0500997 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700998 {"Id", std::to_string(*id)},
999 {"Message", *message},
1000 {"EntryType", "Event"},
1001 {"Severity",
1002 translateSeverityDbusToRedfish(*severity)},
1003 {"Created", crow::utility::getDateTime(timestamp)}};
1004 }
1005 }
1006 std::sort(entriesArray.begin(), entriesArray.end(),
1007 [](const nlohmann::json &left,
1008 const nlohmann::json &right) {
1009 return (left["Id"] <= right["Id"]);
1010 });
1011 asyncResp->res.jsonValue["Members@odata.count"] =
1012 entriesArray.size();
1013 },
1014 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1015 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001016 }
1017};
1018
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001019class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001020{
1021 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001022 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001023 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001024 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1025 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001026 {
1027 entityPrivileges = {
1028 {boost::beast::http::verb::get, {{"Login"}}},
1029 {boost::beast::http::verb::head, {{"Login"}}},
1030 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1031 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1032 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1033 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1034 }
1035
1036 private:
1037 void doGet(crow::Response &res, const crow::Request &req,
1038 const std::vector<std::string> &params) override
1039 {
1040 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001041 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001042 {
1043 messages::internalError(asyncResp->res);
1044 return;
1045 }
Ed Tanous029573d2019-02-01 10:57:49 -08001046 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001047
Andrew Geisslercb92c032018-08-17 07:56:14 -07001048 // DBus implementation of EventLog/Entries
1049 // Make call to Logging Service to find all log entry objects
1050 crow::connections::systemBus->async_method_call(
1051 [asyncResp, entryID](const boost::system::error_code ec,
1052 GetManagedPropertyType &resp) {
1053 if (ec)
1054 {
1055 BMCWEB_LOG_ERROR
1056 << "EventLogEntry (DBus) resp_handler got error " << ec;
1057 messages::internalError(asyncResp->res);
1058 return;
1059 }
Ed Tanous66664f22019-10-11 13:05:49 -07001060 uint32_t *id = nullptr;
1061 std::time_t timestamp{};
1062 std::string *severity = nullptr;
1063 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001064 for (auto &propertyMap : resp)
1065 {
1066 if (propertyMap.first == "Id")
1067 {
1068 id = std::get_if<uint32_t>(&propertyMap.second);
1069 if (id == nullptr)
1070 {
1071 messages::propertyMissing(asyncResp->res, "Id");
1072 }
1073 }
1074 else if (propertyMap.first == "Timestamp")
1075 {
1076 const uint64_t *millisTimeStamp =
1077 std::get_if<uint64_t>(&propertyMap.second);
1078 if (millisTimeStamp == nullptr)
1079 {
1080 messages::propertyMissing(asyncResp->res,
1081 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001082 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001083 }
1084 // Retrieve Created property with format:
1085 // yyyy-mm-ddThh:mm:ss
1086 std::chrono::milliseconds chronoTimeStamp(
1087 *millisTimeStamp);
1088 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001089 std::chrono::duration_cast<
1090 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001091 .count();
1092 }
1093 else if (propertyMap.first == "Severity")
1094 {
1095 severity =
1096 std::get_if<std::string>(&propertyMap.second);
1097 if (severity == nullptr)
1098 {
1099 messages::propertyMissing(asyncResp->res,
1100 "Severity");
1101 }
1102 }
1103 else if (propertyMap.first == "Message")
1104 {
1105 message = std::get_if<std::string>(&propertyMap.second);
1106 if (message == nullptr)
1107 {
1108 messages::propertyMissing(asyncResp->res,
1109 "Message");
1110 }
1111 }
1112 }
Ed Tanous271584a2019-07-09 16:24:22 -07001113 if (id == nullptr || message == nullptr || severity == nullptr)
1114 {
1115 return;
1116 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001117 asyncResp->res.jsonValue = {
1118 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001119 {"@odata.id",
1120 "/redfish/v1/Systems/system/LogServices/EventLog/"
1121 "Entries/" +
1122 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001123 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001124 {"Id", std::to_string(*id)},
1125 {"Message", *message},
1126 {"EntryType", "Event"},
1127 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001128 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001129 },
1130 "xyz.openbmc_project.Logging",
1131 "/xyz/openbmc_project/logging/entry/" + entryID,
1132 "org.freedesktop.DBus.Properties", "GetAll",
1133 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001134 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001135
1136 void doDelete(crow::Response &res, const crow::Request &req,
1137 const std::vector<std::string> &params) override
1138 {
1139
1140 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1141
1142 auto asyncResp = std::make_shared<AsyncResp>(res);
1143
1144 if (params.size() != 1)
1145 {
1146 messages::internalError(asyncResp->res);
1147 return;
1148 }
1149 std::string entryID = params[0];
1150
1151 dbus::utility::escapePathForDbus(entryID);
1152
1153 // Process response from Logging service.
1154 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1155 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1156 if (ec)
1157 {
1158 // TODO Handle for specific error code
1159 BMCWEB_LOG_ERROR
1160 << "EventLogEntry (DBus) doDelete respHandler got error "
1161 << ec;
1162 asyncResp->res.result(
1163 boost::beast::http::status::internal_server_error);
1164 return;
1165 }
1166
1167 asyncResp->res.result(boost::beast::http::status::ok);
1168 };
1169
1170 // Make call to Logging service to request Delete Log
1171 crow::connections::systemBus->async_method_call(
1172 respHandler, "xyz.openbmc_project.Logging",
1173 "/xyz/openbmc_project/logging/entry/" + entryID,
1174 "xyz.openbmc_project.Object.Delete", "Delete");
1175 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001176};
1177
1178class BMCLogServiceCollection : public Node
1179{
1180 public:
1181 template <typename CrowApp>
1182 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001183 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001184 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001185 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001186 {boost::beast::http::verb::get, {{"Login"}}},
1187 {boost::beast::http::verb::head, {{"Login"}}},
1188 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1189 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1190 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1191 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001192 }
1193
1194 private:
1195 /**
1196 * Functions triggers appropriate requests on DBus
1197 */
1198 void doGet(crow::Response &res, const crow::Request &req,
1199 const std::vector<std::string> &params) override
1200 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001201 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001202 // Collections don't include the static data added by SubRoute because
1203 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001204 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001205 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001206 asyncResp->res.jsonValue["@odata.id"] =
1207 "/redfish/v1/Managers/bmc/LogServices";
1208 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1209 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001210 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001211 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1212 logServiceArray = nlohmann::json::array();
1213#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1214 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001215 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001216#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001217 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001218 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001219 }
1220};
1221
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001222class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001223{
1224 public:
1225 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001226 BMCJournalLogService(CrowApp &app) :
1227 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001228 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001229 entityPrivileges = {
1230 {boost::beast::http::verb::get, {{"Login"}}},
1231 {boost::beast::http::verb::head, {{"Login"}}},
1232 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1233 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1234 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1235 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1236 }
1237
1238 private:
1239 void doGet(crow::Response &res, const crow::Request &req,
1240 const std::vector<std::string> &params) override
1241 {
1242 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001243 asyncResp->res.jsonValue["@odata.type"] =
1244 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001245 asyncResp->res.jsonValue["@odata.id"] =
1246 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001247 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1248 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1249 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001250 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001251 asyncResp->res.jsonValue["Entries"] = {
1252 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001253 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001254 }
1255};
1256
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001257static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1258 sd_journal *journal,
1259 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001260{
1261 // Get the Log Entry contents
1262 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001263
Ed Tanous39e77502019-03-04 17:35:53 -08001264 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001265 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001266 if (ret < 0)
1267 {
1268 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1269 return 1;
1270 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001271
1272 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001273 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001274 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001275 if (ret < 0)
1276 {
1277 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001278 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001279
1280 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001281 std::string entryTimeStr;
1282 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001283 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001284 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001285 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001286
1287 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001288 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001289 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001290 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1291 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001292 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001293 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001294 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001295 {"EntryType", "Oem"},
1296 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001297 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001298 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001299 {"Created", std::move(entryTimeStr)}};
1300 return 0;
1301}
1302
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001303class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001304{
1305 public:
1306 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001307 BMCJournalLogEntryCollection(CrowApp &app) :
1308 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001309 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001310 entityPrivileges = {
1311 {boost::beast::http::verb::get, {{"Login"}}},
1312 {boost::beast::http::verb::head, {{"Login"}}},
1313 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1314 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1315 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1316 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1317 }
1318
1319 private:
1320 void doGet(crow::Response &res, const crow::Request &req,
1321 const std::vector<std::string> &params) override
1322 {
1323 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001324 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001325 uint64_t skip = 0;
1326 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001327 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001328 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001329 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001330 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001331 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001332 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001333 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001334 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001335 // Collections don't include the static data added by SubRoute because
1336 // it has a duplicate entry for members
1337 asyncResp->res.jsonValue["@odata.type"] =
1338 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001339 asyncResp->res.jsonValue["@odata.id"] =
1340 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001341 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001342 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001343 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1344 asyncResp->res.jsonValue["Description"] =
1345 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001346 asyncResp->res.jsonValue["@odata.id"] =
1347 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001348 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1349 logEntryArray = nlohmann::json::array();
1350
1351 // Go through the journal and use the timestamp to create a unique ID
1352 // for each entry
1353 sd_journal *journalTmp = nullptr;
1354 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1355 if (ret < 0)
1356 {
1357 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001358 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001359 return;
1360 }
1361 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1362 journalTmp, sd_journal_close);
1363 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001364 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001365 // Reset the unique ID on the first entry
1366 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001367 SD_JOURNAL_FOREACH(journal.get())
1368 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001369 entryCount++;
1370 // Handle paging using skip (number of entries to skip from the
1371 // start) and top (number of entries to display)
1372 if (entryCount <= skip || entryCount > skip + top)
1373 {
1374 continue;
1375 }
1376
Jason M. Bills16428a12018-11-02 12:42:29 -07001377 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001378 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001379 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001380 continue;
1381 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001382
Jason M. Billse85d6b12019-07-29 17:01:15 -07001383 if (firstEntry)
1384 {
1385 firstEntry = false;
1386 }
1387
Jason M. Billse1f26342018-07-18 12:12:00 -07001388 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001389 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1390 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1391 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001392 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001393 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001394 return;
1395 }
1396 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001397 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1398 if (skip + top < entryCount)
1399 {
1400 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001401 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001402 std::to_string(skip + top);
1403 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001404 }
1405};
1406
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001407class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001408{
1409 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001410 BMCJournalLogEntry(CrowApp &app) :
1411 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001412 std::string())
1413 {
1414 entityPrivileges = {
1415 {boost::beast::http::verb::get, {{"Login"}}},
1416 {boost::beast::http::verb::head, {{"Login"}}},
1417 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1418 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1419 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1420 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1421 }
1422
1423 private:
1424 void doGet(crow::Response &res, const crow::Request &req,
1425 const std::vector<std::string> &params) override
1426 {
1427 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1428 if (params.size() != 1)
1429 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001430 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001431 return;
1432 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001433 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001434 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001435 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001436 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001437 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001438 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001439 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001440 }
1441
1442 sd_journal *journalTmp = nullptr;
1443 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1444 if (ret < 0)
1445 {
1446 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001447 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001448 return;
1449 }
1450 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1451 journalTmp, sd_journal_close);
1452 journalTmp = nullptr;
1453 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001454 // tracking the unique ID
1455 std::string idStr;
1456 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001457 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001458 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001459 {
1460 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001461 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1462 {
1463 messages::internalError(asyncResp->res);
1464 return;
1465 }
1466 if (firstEntry)
1467 {
1468 firstEntry = false;
1469 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001470 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001471 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001472 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001473 {
1474 messages::resourceMissingAtURI(asyncResp->res, entryID);
1475 return;
1476 }
1477
1478 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1479 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001480 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001481 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001482 return;
1483 }
1484 }
1485};
1486
Jason M. Bills424c4172019-03-21 13:50:33 -07001487class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001488{
1489 public:
1490 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001491 CrashdumpService(CrowApp &app) :
1492 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001493 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001494 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001495 {boost::beast::http::verb::get, {{"Login"}}},
1496 {boost::beast::http::verb::head, {{"Login"}}},
1497 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1498 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1499 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1500 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001501 }
1502
1503 private:
1504 /**
1505 * Functions triggers appropriate requests on DBus
1506 */
1507 void doGet(crow::Response &res, const crow::Request &req,
1508 const std::vector<std::string> &params) override
1509 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001510 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001511 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001512 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001513 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001514 asyncResp->res.jsonValue["@odata.type"] =
1515 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001516 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1517 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1518 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001519 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1520 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001521 asyncResp->res.jsonValue["Entries"] = {
1522 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001523 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001524 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001525 {"#LogService.ClearLog",
1526 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1527 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001528 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001529 {{"#Crashdump.OnDemand",
1530 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1531 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001532
1533#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001534 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001535 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001536 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1537 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001538#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001539 }
1540};
1541
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001542class CrashdumpClear : public Node
1543{
1544 public:
1545 CrashdumpClear(CrowApp &app) :
1546 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1547 "LogService.ClearLog/")
1548 {
1549 entityPrivileges = {
1550 {boost::beast::http::verb::get, {{"Login"}}},
1551 {boost::beast::http::verb::head, {{"Login"}}},
1552 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1553 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1554 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1555 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1556 }
1557
1558 private:
1559 void doPost(crow::Response &res, const crow::Request &req,
1560 const std::vector<std::string> &params) override
1561 {
1562 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1563
1564 crow::connections::systemBus->async_method_call(
1565 [asyncResp](const boost::system::error_code ec,
1566 const std::string &resp) {
1567 if (ec)
1568 {
1569 messages::internalError(asyncResp->res);
1570 return;
1571 }
1572 messages::success(asyncResp->res);
1573 },
1574 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
1575 }
1576};
1577
Jason M. Billse855dd22019-10-08 11:37:48 -07001578std::string getLogCreatedTime(const std::string &crashdump)
1579{
1580 nlohmann::json crashdumpJson =
1581 nlohmann::json::parse(crashdump, nullptr, false);
1582 if (crashdumpJson.is_discarded())
1583 {
1584 return std::string();
1585 }
1586
1587 nlohmann::json::const_iterator cdIt = crashdumpJson.find("crash_data");
1588 if (cdIt == crashdumpJson.end())
1589 {
1590 return std::string();
1591 }
1592
1593 nlohmann::json::const_iterator siIt = cdIt->find("METADATA");
1594 if (siIt == cdIt->end())
1595 {
1596 return std::string();
1597 }
1598
1599 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1600 if (tsIt == siIt->end())
1601 {
1602 return std::string();
1603 }
1604
1605 const std::string *logTime = tsIt->get_ptr<const std::string *>();
1606 if (logTime == nullptr)
1607 {
1608 return std::string();
1609 }
1610
1611 std::string redfishDateTime = *logTime;
1612 if (redfishDateTime.length() > 2)
1613 {
1614 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1615 }
1616
1617 return redfishDateTime;
1618}
1619
1620std::string getLogFileName(const std::string &logTime)
1621{
1622 // Set the crashdump file name to "crashdump_<logTime>.json" using the
1623 // created time without the timezone info
1624 std::string fileTime = logTime;
1625 size_t plusPos = fileTime.rfind('+');
1626 if (plusPos != std::string::npos)
1627 {
1628 fileTime.erase(plusPos);
1629 }
1630 return "crashdump_" + fileTime + ".json";
1631}
1632
1633static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1634 const std::string &logID,
1635 nlohmann::json &logEntryJson)
1636{
1637 auto getStoredLogCallback = [asyncResp, logID, &logEntryJson](
1638 const boost::system::error_code ec,
1639 const std::variant<std::string> &resp) {
1640 if (ec)
1641 {
1642 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001643 if (ec.value() ==
1644 boost::system::linux_error::bad_request_descriptor)
1645 {
1646 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
1647 }
1648 else
1649 {
1650 messages::internalError(asyncResp->res);
1651 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001652 return;
1653 }
1654 const std::string *log = std::get_if<std::string>(&resp);
1655 if (log == nullptr)
1656 {
1657 messages::internalError(asyncResp->res);
1658 return;
1659 }
1660 std::string logTime = getLogCreatedTime(*log);
1661 std::string fileName = getLogFileName(logTime);
1662
1663 logEntryJson = {
1664 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse855dd22019-10-08 11:37:48 -07001665 {"@odata.id",
1666 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1667 logID},
1668 {"Name", "CPU Crashdump"},
1669 {"Id", logID},
1670 {"EntryType", "Oem"},
1671 {"OemRecordFormat", "Crashdump URI"},
1672 {"Message",
1673 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1674 logID + "/" + fileName},
1675 {"Created", std::move(logTime)}};
1676 };
1677 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001678 std::move(getStoredLogCallback), crashdumpObject,
1679 crashdumpPath + std::string("/") + logID,
1680 "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, "Log");
Jason M. Billse855dd22019-10-08 11:37:48 -07001681}
1682
Jason M. Bills424c4172019-03-21 13:50:33 -07001683class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001684{
1685 public:
1686 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001687 CrashdumpEntryCollection(CrowApp &app) :
1688 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001689 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001690 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001691 {boost::beast::http::verb::get, {{"Login"}}},
1692 {boost::beast::http::verb::head, {{"Login"}}},
1693 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1694 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1695 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1696 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001697 }
1698
1699 private:
1700 /**
1701 * Functions triggers appropriate requests on DBus
1702 */
1703 void doGet(crow::Response &res, const crow::Request &req,
1704 const std::vector<std::string> &params) override
1705 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001706 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001707 // Collections don't include the static data added by SubRoute because
1708 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001709 auto getLogEntriesCallback = [asyncResp](
1710 const boost::system::error_code ec,
1711 const std::vector<std::string> &resp) {
1712 if (ec)
1713 {
1714 if (ec.value() !=
1715 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001716 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001717 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1718 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001719 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001720 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001721 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001722 }
1723 asyncResp->res.jsonValue["@odata.type"] =
1724 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001725 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001726 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07001727 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001728 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001729 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001730 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1731 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07001732 std::vector<std::string> logIDs;
1733 // Get the list of log entries and build up an empty array big
1734 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07001735 for (const std::string &objpath : resp)
1736 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001737 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07001738 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07001739 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07001740 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001741 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07001742 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001743 logIDs.emplace_back(objpath.substr(lastPos + 1));
1744
1745 // Add a space for the log entry to the array
1746 logEntryArray.push_back({});
1747 }
1748 // Now go through and set up async calls to fill in the entries
1749 size_t index = 0;
1750 for (const std::string &logID : logIDs)
1751 {
1752 // Add the log entry to the array
1753 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07001754 }
1755 asyncResp->res.jsonValue["Members@odata.count"] =
1756 logEntryArray.size();
1757 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001758 crow::connections::systemBus->async_method_call(
1759 std::move(getLogEntriesCallback),
1760 "xyz.openbmc_project.ObjectMapper",
1761 "/xyz/openbmc_project/object_mapper",
1762 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001763 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001764 }
1765};
1766
Jason M. Bills424c4172019-03-21 13:50:33 -07001767class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001768{
1769 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001770 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001771 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001772 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001773 std::string())
1774 {
1775 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001776 {boost::beast::http::verb::get, {{"Login"}}},
1777 {boost::beast::http::verb::head, {{"Login"}}},
1778 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1779 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1780 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1781 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001782 }
1783
1784 private:
1785 void doGet(crow::Response &res, const crow::Request &req,
1786 const std::vector<std::string> &params) override
1787 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001788 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001789 if (params.size() != 1)
1790 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001791 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001792 return;
1793 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001794 const std::string &logID = params[0];
1795 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
1796 }
1797};
1798
1799class CrashdumpFile : public Node
1800{
1801 public:
1802 CrashdumpFile(CrowApp &app) :
1803 Node(app,
1804 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
1805 "<str>/",
1806 std::string(), std::string())
1807 {
1808 entityPrivileges = {
1809 {boost::beast::http::verb::get, {{"Login"}}},
1810 {boost::beast::http::verb::head, {{"Login"}}},
1811 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1812 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1813 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1814 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1815 }
1816
1817 private:
1818 void doGet(crow::Response &res, const crow::Request &req,
1819 const std::vector<std::string> &params) override
1820 {
1821 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1822 if (params.size() != 2)
1823 {
1824 messages::internalError(asyncResp->res);
1825 return;
1826 }
1827 const std::string &logID = params[0];
1828 const std::string &fileName = params[1];
1829
1830 auto getStoredLogCallback = [asyncResp, logID, fileName](
Ed Tanousabf2add2019-01-22 16:40:12 -08001831 const boost::system::error_code ec,
1832 const std::variant<std::string> &resp) {
1833 if (ec)
1834 {
1835 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1836 messages::internalError(asyncResp->res);
1837 return;
1838 }
1839 const std::string *log = std::get_if<std::string>(&resp);
1840 if (log == nullptr)
1841 {
1842 messages::internalError(asyncResp->res);
1843 return;
1844 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001845
1846 // Verify the file name parameter is correct
1847 if (fileName != getLogFileName(getLogCreatedTime(*log)))
Ed Tanousabf2add2019-01-22 16:40:12 -08001848 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001849 messages::resourceMissingAtURI(asyncResp->res, fileName);
Ed Tanousabf2add2019-01-22 16:40:12 -08001850 return;
1851 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001852
1853 // Configure this to be a file download when accessed from a browser
1854 asyncResp->res.addHeader("Content-Disposition", "attachment");
1855 asyncResp->res.body() = *log;
Ed Tanousabf2add2019-01-22 16:40:12 -08001856 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001857 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001858 std::move(getStoredLogCallback), crashdumpObject,
1859 crashdumpPath + std::string("/") + logID,
1860 "org.freedesktop.DBus.Properties", "Get", crashdumpInterface,
Jason M. Bills424c4172019-03-21 13:50:33 -07001861 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001862 }
1863};
1864
Jason M. Bills424c4172019-03-21 13:50:33 -07001865class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001866{
1867 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001868 OnDemandCrashdump(CrowApp &app) :
1869 Node(app,
1870 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1871 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001872 {
1873 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001874 {boost::beast::http::verb::get, {{"Login"}}},
1875 {boost::beast::http::verb::head, {{"Login"}}},
1876 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1877 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1878 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1879 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001880 }
1881
1882 private:
1883 void doPost(crow::Response &res, const crow::Request &req,
1884 const std::vector<std::string> &params) override
1885 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001886 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001887
James Feistfe306722020-03-12 16:32:08 -07001888 auto generateonDemandLogCallback = [asyncResp,
1889 req](const boost::system::error_code
1890 ec,
1891 const std::string &resp) {
James Feist46229572020-02-19 15:11:58 -08001892 if (ec)
1893 {
1894 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07001895 {
James Feist46229572020-02-19 15:11:58 -08001896 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001897 }
James Feist46229572020-02-19 15:11:58 -08001898 else if (ec.value() ==
1899 boost::system::errc::device_or_resource_busy)
1900 {
1901 messages::serviceTemporarilyUnavailable(asyncResp->res,
1902 "60");
1903 }
1904 else
1905 {
1906 messages::internalError(asyncResp->res);
1907 }
1908 return;
1909 }
1910 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08001911 [](boost::system::error_code err, sdbusplus::message::message &,
1912 const std::shared_ptr<task::TaskData> &taskData) {
1913 if (!err)
1914 {
1915 taskData->messages.emplace_back(messages::success());
James Feist831d6b02020-03-12 16:31:30 -07001916 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08001917 }
James Feist32898ce2020-03-10 16:16:52 -07001918 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08001919 },
James Feist46229572020-02-19 15:11:58 -08001920 "type='signal',interface='org.freedesktop.DBus.Properties',"
1921 "member='PropertiesChanged',arg0namespace='com.intel."
1922 "crashdump'");
1923 task->startTimer(std::chrono::minutes(5));
1924 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07001925 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08001926 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001927 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001928 std::move(generateonDemandLogCallback), crashdumpObject,
1929 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001930 }
1931};
1932
Jason M. Billse1f26342018-07-18 12:12:00 -07001933class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001934{
1935 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001936 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001937 Node(app,
1938 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1939 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001940 {
1941 entityPrivileges = {
1942 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1943 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1944 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1945 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1946 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1947 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1948 }
1949
1950 private:
1951 void doPost(crow::Response &res, const crow::Request &req,
1952 const std::vector<std::string> &params) override
1953 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001954 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001955 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07001956
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001957 nlohmann::json reqJson =
1958 nlohmann::json::parse(req.body, nullptr, false);
1959 if (reqJson.find("PECICommands") != reqJson.end())
1960 {
1961 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
1962 {
1963 return;
1964 }
1965 uint32_t idx = 0;
1966 for (auto const &cmd : peciCommands)
1967 {
1968 if (cmd.size() < 3)
1969 {
1970 std::string s("[");
1971 for (auto const &val : cmd)
1972 {
1973 if (val != *cmd.begin())
1974 {
1975 s += ",";
1976 }
1977 s += std::to_string(val);
1978 }
1979 s += "]";
1980 messages::actionParameterValueFormatError(
1981 res, s, "PECICommands[" + std::to_string(idx) + "]",
1982 "SendRawPeci");
1983 return;
1984 }
1985 idx++;
1986 }
1987 }
1988 else
1989 {
1990 /* This interface is deprecated */
1991 uint8_t clientAddress = 0;
1992 uint8_t readLength = 0;
1993 std::vector<uint8_t> peciCommand;
1994 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1995 "ReadLength", readLength, "PECICommand",
1996 peciCommand))
1997 {
1998 return;
1999 }
2000 peciCommands.push_back({clientAddress, 0, readLength});
2001 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2002 peciCommand.end());
2003 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002004 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002005 auto sendRawPECICallback =
2006 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002007 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002008 if (ec)
2009 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002010 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002011 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002012 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002013 return;
2014 }
2015 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2016 {"PECIResponse", resp}};
2017 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002018 // Call the SendRawPECI command with the provided data
2019 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002020 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002021 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002022 }
2023};
2024
Andrew Geisslercb92c032018-08-17 07:56:14 -07002025/**
2026 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2027 */
2028class DBusLogServiceActionsClear : public Node
2029{
2030 public:
2031 DBusLogServiceActionsClear(CrowApp &app) :
2032 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Tim Lee1f56a3a2019-10-09 10:17:57 +08002033 "LogService.ClearLog")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002034 {
2035 entityPrivileges = {
2036 {boost::beast::http::verb::get, {{"Login"}}},
2037 {boost::beast::http::verb::head, {{"Login"}}},
2038 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2039 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2040 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2041 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2042 }
2043
2044 private:
2045 /**
2046 * Function handles POST method request.
2047 * The Clear Log actions does not require any parameter.The action deletes
2048 * all entries found in the Entries collection for this Log Service.
2049 */
2050 void doPost(crow::Response &res, const crow::Request &req,
2051 const std::vector<std::string> &params) override
2052 {
2053 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2054
2055 auto asyncResp = std::make_shared<AsyncResp>(res);
2056 // Process response from Logging service.
2057 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2058 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2059 if (ec)
2060 {
2061 // TODO Handle for specific error code
2062 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2063 asyncResp->res.result(
2064 boost::beast::http::status::internal_server_error);
2065 return;
2066 }
2067
2068 asyncResp->res.result(boost::beast::http::status::no_content);
2069 };
2070
2071 // Make call to Logging service to request Clear Log
2072 crow::connections::systemBus->async_method_call(
2073 resp_handler, "xyz.openbmc_project.Logging",
2074 "/xyz/openbmc_project/logging",
2075 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2076 }
2077};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002078
2079/****************************************************
2080 * Redfish PostCode interfaces
2081 * using DBUS interface: getPostCodesTS
2082 ******************************************************/
2083class PostCodesLogService : public Node
2084{
2085 public:
2086 PostCodesLogService(CrowApp &app) :
2087 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2088 {
2089 entityPrivileges = {
2090 {boost::beast::http::verb::get, {{"Login"}}},
2091 {boost::beast::http::verb::head, {{"Login"}}},
2092 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2093 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2094 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2095 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2096 }
2097
2098 private:
2099 void doGet(crow::Response &res, const crow::Request &req,
2100 const std::vector<std::string> &params) override
2101 {
2102 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2103
2104 asyncResp->res.jsonValue = {
2105 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2106 {"@odata.type", "#LogService.v1_1_0.LogService"},
2107 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2108 {"Name", "POST Code Log Service"},
2109 {"Description", "POST Code Log Service"},
2110 {"Id", "BIOS POST Code Log"},
2111 {"OverWritePolicy", "WrapsWhenFull"},
2112 {"Entries",
2113 {{"@odata.id",
2114 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2115 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2116 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2117 "Actions/LogService.ClearLog"}};
2118 }
2119};
2120
2121class PostCodesClear : public Node
2122{
2123 public:
2124 PostCodesClear(CrowApp &app) :
2125 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2126 "LogService.ClearLog/")
2127 {
2128 entityPrivileges = {
2129 {boost::beast::http::verb::get, {{"Login"}}},
2130 {boost::beast::http::verb::head, {{"Login"}}},
2131 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2132 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2133 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2134 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2135 }
2136
2137 private:
2138 void doPost(crow::Response &res, const crow::Request &req,
2139 const std::vector<std::string> &params) override
2140 {
2141 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2142
2143 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2144 // Make call to post-code service to request clear all
2145 crow::connections::systemBus->async_method_call(
2146 [asyncResp](const boost::system::error_code ec) {
2147 if (ec)
2148 {
2149 // TODO Handle for specific error code
2150 BMCWEB_LOG_ERROR
2151 << "doClearPostCodes resp_handler got error " << ec;
2152 asyncResp->res.result(
2153 boost::beast::http::status::internal_server_error);
2154 messages::internalError(asyncResp->res);
2155 return;
2156 }
2157 },
2158 "xyz.openbmc_project.State.Boot.PostCode",
2159 "/xyz/openbmc_project/State/Boot/PostCode",
2160 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2161 }
2162};
2163
2164static void fillPostCodeEntry(
2165 std::shared_ptr<AsyncResp> aResp,
2166 const boost::container::flat_map<uint64_t, uint64_t> &postcode,
2167 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2168 const uint64_t skip = 0, const uint64_t top = 0)
2169{
2170 // Get the Message from the MessageRegistry
2171 const message_registries::Message *message =
2172 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
2173 std::string severity;
2174 if (message != nullptr)
2175 {
2176 severity = message->severity;
2177 }
2178
2179 uint64_t currentCodeIndex = 0;
2180 nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"];
2181
2182 uint64_t firstCodeTimeUs = 0;
2183 for (const std::pair<uint64_t, uint64_t> &code : postcode)
2184 {
2185 currentCodeIndex++;
2186 std::string postcodeEntryID =
2187 "B" + std::to_string(bootIndex) + "-" +
2188 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2189
2190 uint64_t usecSinceEpoch = code.first;
2191 uint64_t usTimeOffset = 0;
2192
2193 if (1 == currentCodeIndex)
2194 { // already incremented
2195 firstCodeTimeUs = code.first;
2196 }
2197 else
2198 {
2199 usTimeOffset = code.first - firstCodeTimeUs;
2200 }
2201
2202 // skip if no specific codeIndex is specified and currentCodeIndex does
2203 // not fall between top and skip
2204 if ((codeIndex == 0) &&
2205 (currentCodeIndex <= skip || currentCodeIndex > top))
2206 {
2207 continue;
2208 }
2209
2210 // skip if a sepcific codeIndex is specified and does not match the
2211 // currentIndex
2212 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2213 {
2214 // This is done for simplicity. 1st entry is needed to calculate
2215 // time offset. To improve efficiency, one can get to the entry
2216 // directly (possibly with flatmap's nth method)
2217 continue;
2218 }
2219
2220 // currentCodeIndex is within top and skip or equal to specified code
2221 // index
2222
2223 // Get the Created time from the timestamp
2224 std::string entryTimeStr;
2225 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2226 {
2227 continue;
2228 }
2229
2230 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2231 std::ostringstream hexCode;
2232 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2233 << code.second;
2234 std::ostringstream timeOffsetStr;
2235 // Set Fixed -Point Notation
2236 timeOffsetStr << std::fixed;
2237 // Set precision to 4 digits
2238 timeOffsetStr << std::setprecision(4);
2239 // Add double to stream
2240 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2241 std::vector<std::string> messageArgs = {
2242 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2243
2244 // Get MessageArgs template from message registry
2245 std::string msg;
2246 if (message != nullptr)
2247 {
2248 msg = message->message;
2249
2250 // fill in this post code value
2251 int i = 0;
2252 for (const std::string &messageArg : messageArgs)
2253 {
2254 std::string argStr = "%" + std::to_string(++i);
2255 size_t argPos = msg.find(argStr);
2256 if (argPos != std::string::npos)
2257 {
2258 msg.replace(argPos, argStr.length(), messageArg);
2259 }
2260 }
2261 }
2262
2263 // add to AsyncResp
2264 logEntryArray.push_back({});
2265 nlohmann::json &bmcLogEntry = logEntryArray.back();
2266 bmcLogEntry = {
2267 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2268 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2269 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2270 "PostCodes/Entries/" +
2271 postcodeEntryID},
2272 {"Name", "POST Code Log Entry"},
2273 {"Id", postcodeEntryID},
2274 {"Message", std::move(msg)},
2275 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2276 {"MessageArgs", std::move(messageArgs)},
2277 {"EntryType", "Event"},
2278 {"Severity", std::move(severity)},
2279 {"Created", std::move(entryTimeStr)}};
2280 }
2281}
2282
2283static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2284 const uint16_t bootIndex,
2285 const uint64_t codeIndex)
2286{
2287 crow::connections::systemBus->async_method_call(
2288 [aResp, bootIndex, codeIndex](
2289 const boost::system::error_code ec,
2290 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2291 if (ec)
2292 {
2293 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2294 messages::internalError(aResp->res);
2295 return;
2296 }
2297
2298 // skip the empty postcode boots
2299 if (postcode.empty())
2300 {
2301 return;
2302 }
2303
2304 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2305
2306 aResp->res.jsonValue["Members@odata.count"] =
2307 aResp->res.jsonValue["Members"].size();
2308 },
2309 "xyz.openbmc_project.State.Boot.PostCode",
2310 "/xyz/openbmc_project/State/Boot/PostCode",
2311 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2312 bootIndex);
2313}
2314
2315static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2316 const uint16_t bootIndex,
2317 const uint16_t bootCount,
2318 const uint64_t entryCount, const uint64_t skip,
2319 const uint64_t top)
2320{
2321 crow::connections::systemBus->async_method_call(
2322 [aResp, bootIndex, bootCount, entryCount, skip,
2323 top](const boost::system::error_code ec,
2324 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2325 if (ec)
2326 {
2327 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2328 messages::internalError(aResp->res);
2329 return;
2330 }
2331
2332 uint64_t endCount = entryCount;
2333 if (!postcode.empty())
2334 {
2335 endCount = entryCount + postcode.size();
2336
2337 if ((skip < endCount) && ((top + skip) > entryCount))
2338 {
2339 uint64_t thisBootSkip =
2340 std::max(skip, entryCount) - entryCount;
2341 uint64_t thisBootTop =
2342 std::min(top + skip, endCount) - entryCount;
2343
2344 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2345 thisBootSkip, thisBootTop);
2346 }
2347 aResp->res.jsonValue["Members@odata.count"] = endCount;
2348 }
2349
2350 // continue to previous bootIndex
2351 if (bootIndex < bootCount)
2352 {
2353 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2354 bootCount, endCount, skip, top);
2355 }
2356 else
2357 {
2358 aResp->res.jsonValue["Members@odata.nextLink"] =
2359 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2360 "Entries?$skip=" +
2361 std::to_string(skip + top);
2362 }
2363 },
2364 "xyz.openbmc_project.State.Boot.PostCode",
2365 "/xyz/openbmc_project/State/Boot/PostCode",
2366 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2367 bootIndex);
2368}
2369
2370static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2371 const uint64_t skip, const uint64_t top)
2372{
2373 uint64_t entryCount = 0;
2374 crow::connections::systemBus->async_method_call(
2375 [aResp, entryCount, skip,
2376 top](const boost::system::error_code ec,
2377 const std::variant<uint16_t> &bootCount) {
2378 if (ec)
2379 {
2380 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2381 messages::internalError(aResp->res);
2382 return;
2383 }
2384 auto pVal = std::get_if<uint16_t>(&bootCount);
2385 if (pVal)
2386 {
2387 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2388 }
2389 else
2390 {
2391 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2392 }
2393 },
2394 "xyz.openbmc_project.State.Boot.PostCode",
2395 "/xyz/openbmc_project/State/Boot/PostCode",
2396 "org.freedesktop.DBus.Properties", "Get",
2397 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2398}
2399
2400class PostCodesEntryCollection : public Node
2401{
2402 public:
2403 template <typename CrowApp>
2404 PostCodesEntryCollection(CrowApp &app) :
2405 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2406 {
2407 entityPrivileges = {
2408 {boost::beast::http::verb::get, {{"Login"}}},
2409 {boost::beast::http::verb::head, {{"Login"}}},
2410 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2411 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2412 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2413 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2414 }
2415
2416 private:
2417 void doGet(crow::Response &res, const crow::Request &req,
2418 const std::vector<std::string> &params) override
2419 {
2420 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2421
2422 asyncResp->res.jsonValue["@odata.type"] =
2423 "#LogEntryCollection.LogEntryCollection";
2424 asyncResp->res.jsonValue["@odata.context"] =
2425 "/redfish/v1/"
2426 "$metadata#LogEntryCollection.LogEntryCollection";
2427 asyncResp->res.jsonValue["@odata.id"] =
2428 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2429 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2430 asyncResp->res.jsonValue["Description"] =
2431 "Collection of POST Code Log Entries";
2432 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2433 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2434
2435 uint64_t skip = 0;
2436 uint64_t top = maxEntriesPerPage; // Show max entries by default
2437 if (!getSkipParam(asyncResp->res, req, skip))
2438 {
2439 return;
2440 }
2441 if (!getTopParam(asyncResp->res, req, top))
2442 {
2443 return;
2444 }
2445 getCurrentBootNumber(asyncResp, skip, top);
2446 }
2447};
2448
2449class PostCodesEntry : public Node
2450{
2451 public:
2452 PostCodesEntry(CrowApp &app) :
2453 Node(app,
2454 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2455 std::string())
2456 {
2457 entityPrivileges = {
2458 {boost::beast::http::verb::get, {{"Login"}}},
2459 {boost::beast::http::verb::head, {{"Login"}}},
2460 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2461 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2462 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2463 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2464 }
2465
2466 private:
2467 void doGet(crow::Response &res, const crow::Request &req,
2468 const std::vector<std::string> &params) override
2469 {
2470 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2471 if (params.size() != 1)
2472 {
2473 messages::internalError(asyncResp->res);
2474 return;
2475 }
2476
2477 const std::string &targetID = params[0];
2478
2479 size_t bootPos = targetID.find('B');
2480 if (bootPos == std::string::npos)
2481 {
2482 // Requested ID was not found
2483 messages::resourceMissingAtURI(asyncResp->res, targetID);
2484 return;
2485 }
2486 std::string_view bootIndexStr(targetID);
2487 bootIndexStr.remove_prefix(bootPos + 1);
2488 uint16_t bootIndex = 0;
2489 uint64_t codeIndex = 0;
2490 size_t dashPos = bootIndexStr.find('-');
2491
2492 if (dashPos == std::string::npos)
2493 {
2494 return;
2495 }
2496 std::string_view codeIndexStr(bootIndexStr);
2497 bootIndexStr.remove_suffix(dashPos);
2498 codeIndexStr.remove_prefix(dashPos + 1);
2499
2500 bootIndex = static_cast<uint16_t>(
2501 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2502 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2503 if (bootIndex == 0 || codeIndex == 0)
2504 {
2505 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2506 << params[0];
2507 }
2508
2509 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2510 asyncResp->res.jsonValue["@odata.context"] =
2511 "/redfish/v1/$metadata#LogEntry.LogEntry";
2512 asyncResp->res.jsonValue["@odata.id"] =
2513 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2514 "Entries";
2515 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2516 asyncResp->res.jsonValue["Description"] =
2517 "Collection of POST Code Log Entries";
2518 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2519 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2520
2521 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2522 }
2523};
2524
Ed Tanous1da66f72018-07-27 16:13:37 -07002525} // namespace redfish