blob: bee1a926bb6723e3308ef88a5eee9c05cc0b01b1 [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
raviteja-b06578432020-02-03 12:50:42 -060030#include <dump_offload.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070031#include <error_messages.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050032
James Feist4418c7f2019-04-15 11:09:15 -070033#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070034#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080035#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070036
37namespace redfish
38{
39
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040constexpr char const* crashdumpObject = "com.intel.crashdump";
41constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050042constexpr char const* crashdumpInterface = "com.intel.crashdump";
43constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070044 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050045constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070046 "com.intel.crashdump.OnDemand";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050047constexpr char const* crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070048 "com.intel.crashdump.SendRawPeci";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070049constexpr char const* crashdumpTelemetryInterface =
50 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070051
Jason M. Bills4851d452019-03-28 11:27:48 -070052namespace message_registries
53{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050054static const Message* getMessageFromRegistry(
55 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070056 const boost::beast::span<const MessageEntry> registry)
57{
58 boost::beast::span<const MessageEntry>::const_iterator messageIt =
59 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050060 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070061 return !std::strcmp(messageEntry.first,
62 messageKey.c_str());
63 });
64 if (messageIt != registry.cend())
65 {
66 return &messageIt->second;
67 }
68
69 return nullptr;
70}
71
Gunnar Mills1214b7e2020-06-04 10:11:30 -050072static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070073{
74 // Redfish MessageIds are in the form
75 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
76 // the right Message
77 std::vector<std::string> fields;
78 fields.reserve(4);
79 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050080 std::string& registryName = fields[0];
81 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070082
83 // Find the right registry and check it for the MessageKey
84 if (std::string(base::header.registryPrefix) == registryName)
85 {
86 return getMessageFromRegistry(
87 messageKey, boost::beast::span<const MessageEntry>(base::registry));
88 }
89 if (std::string(openbmc::header.registryPrefix) == registryName)
90 {
91 return getMessageFromRegistry(
92 messageKey,
93 boost::beast::span<const MessageEntry>(openbmc::registry));
94 }
95 return nullptr;
96}
97} // namespace message_registries
98
James Feistf6150402019-01-08 10:36:20 -080099namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700100
Andrew Geisslercb92c032018-08-17 07:56:14 -0700101using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500102 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
103 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700104
105using GetManagedObjectsType = boost::container::flat_map<
106 sdbusplus::message::object_path,
107 boost::container::flat_map<std::string, GetManagedPropertyType>>;
108
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700110{
111 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
112 {
113 return "Critical";
114 }
115 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
116 {
117 return "Critical";
118 }
119 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
120 {
121 return "OK";
122 }
123 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
124 {
125 return "Critical";
126 }
127 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
128 {
129 return "Critical";
130 }
131 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
132 {
133 return "OK";
134 }
135 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
136 {
137 return "OK";
138 }
139 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
140 {
141 return "Warning";
142 }
143 return "";
144}
145
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500146static int getJournalMetadata(sd_journal* journal,
147 const std::string_view& field,
148 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700149{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500150 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700151 size_t length = 0;
152 int ret = 0;
153 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700154 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500155 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700156 if (ret < 0)
157 {
158 return ret;
159 }
Ed Tanous39e77502019-03-04 17:35:53 -0800160 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700161 // Only use the content after the "=" character.
162 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
163 return ret;
164}
165
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500166static int getJournalMetadata(sd_journal* journal,
167 const std::string_view& field, const int& base,
168 long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700169{
170 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800171 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700172 // Get the metadata from the requested field of the journal entry
173 ret = getJournalMetadata(journal, field, metadata);
174 if (ret < 0)
175 {
176 return ret;
177 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000178 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700179 return ret;
180}
181
ZhikuiRena3316fc2020-01-29 14:58:08 -0800182static bool getTimestampStr(const uint64_t usecSinceEpoch,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500183 std::string& entryTimestamp)
Jason M. Bills16428a12018-11-02 12:42:29 -0700184{
ZhikuiRena3316fc2020-01-29 14:58:08 -0800185 time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500186 struct tm* loctime = localtime(&t);
Jason M. Bills16428a12018-11-02 12:42:29 -0700187 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700188 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700189 {
190 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
191 }
192 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800193 std::string_view t1(entryTime);
194 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700195 if (t1.size() > 2 && t2.size() > 2)
196 {
197 t1.remove_suffix(2);
198 t2.remove_prefix(t2.size() - 2);
199 }
Ed Tanous39e77502019-03-04 17:35:53 -0800200 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700201 return true;
202}
203
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800205{
206 int ret = 0;
207 uint64_t timestamp = 0;
208 ret = sd_journal_get_realtime_usec(journal, &timestamp);
209 if (ret < 0)
210 {
211 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
212 << strerror(-ret);
213 return false;
214 }
215 return getTimestampStr(timestamp, entryTimestamp);
216}
217
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500218static bool getSkipParam(crow::Response& res, const crow::Request& req,
219 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700220{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500221 char* skipParam = req.urlParams.get("$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700222 if (skipParam != nullptr)
223 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500224 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700225 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700226 if (*skipParam == '\0' || *ptr != '\0')
227 {
228
229 messages::queryParameterValueTypeError(res, std::string(skipParam),
230 "$skip");
231 return false;
232 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700233 }
234 return true;
235}
236
Ed Tanous271584a2019-07-09 16:24:22 -0700237static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500238static bool getTopParam(crow::Response& res, const crow::Request& req,
239 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700240{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500241 char* topParam = req.urlParams.get("$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700242 if (topParam != nullptr)
243 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700245 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700246 if (*topParam == '\0' || *ptr != '\0')
247 {
248 messages::queryParameterValueTypeError(res, std::string(topParam),
249 "$top");
250 return false;
251 }
Ed Tanous271584a2019-07-09 16:24:22 -0700252 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700253 {
254
255 messages::queryParameterOutOfRange(
256 res, std::to_string(top), "$top",
257 "1-" + std::to_string(maxEntriesPerPage));
258 return false;
259 }
260 }
261 return true;
262}
263
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500264static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700265 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700266{
267 int ret = 0;
268 static uint64_t prevTs = 0;
269 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700270 if (firstEntry)
271 {
272 prevTs = 0;
273 }
274
Jason M. Bills16428a12018-11-02 12:42:29 -0700275 // Get the entry timestamp
276 uint64_t curTs = 0;
277 ret = sd_journal_get_realtime_usec(journal, &curTs);
278 if (ret < 0)
279 {
280 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
281 << strerror(-ret);
282 return false;
283 }
284 // If the timestamp isn't unique, increment the index
285 if (curTs == prevTs)
286 {
287 index++;
288 }
289 else
290 {
291 // Otherwise, reset it
292 index = 0;
293 }
294 // Save the timestamp
295 prevTs = curTs;
296
297 entryID = std::to_string(curTs);
298 if (index > 0)
299 {
300 entryID += "_" + std::to_string(index);
301 }
302 return true;
303}
304
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500305static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700306 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700307{
Ed Tanous271584a2019-07-09 16:24:22 -0700308 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700309 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700310 if (firstEntry)
311 {
312 prevTs = 0;
313 }
314
Jason M. Bills95820182019-04-22 16:25:34 -0700315 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700316 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700317 std::tm timeStruct = {};
318 std::istringstream entryStream(logEntry);
319 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
320 {
321 curTs = std::mktime(&timeStruct);
322 }
323 // If the timestamp isn't unique, increment the index
324 if (curTs == prevTs)
325 {
326 index++;
327 }
328 else
329 {
330 // Otherwise, reset it
331 index = 0;
332 }
333 // Save the timestamp
334 prevTs = curTs;
335
336 entryID = std::to_string(curTs);
337 if (index > 0)
338 {
339 entryID += "_" + std::to_string(index);
340 }
341 return true;
342}
343
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500344static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
345 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700346{
347 if (entryID.empty())
348 {
349 return false;
350 }
351 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800352 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700353
354 auto underscorePos = tsStr.find("_");
355 if (underscorePos != tsStr.npos)
356 {
357 // Timestamp has an index
358 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800359 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700360 indexStr.remove_prefix(underscorePos + 1);
361 std::size_t pos;
362 try
363 {
Ed Tanous39e77502019-03-04 17:35:53 -0800364 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700365 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500366 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700367 {
368 messages::resourceMissingAtURI(res, entryID);
369 return false;
370 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500371 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700372 {
373 messages::resourceMissingAtURI(res, entryID);
374 return false;
375 }
376 if (pos != indexStr.size())
377 {
378 messages::resourceMissingAtURI(res, entryID);
379 return false;
380 }
381 }
382 // Timestamp has no index
383 std::size_t pos;
384 try
385 {
Ed Tanous39e77502019-03-04 17:35:53 -0800386 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700387 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500388 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700389 {
390 messages::resourceMissingAtURI(res, entryID);
391 return false;
392 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500393 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700394 {
395 messages::resourceMissingAtURI(res, entryID);
396 return false;
397 }
398 if (pos != tsStr.size())
399 {
400 messages::resourceMissingAtURI(res, entryID);
401 return false;
402 }
403 return true;
404}
405
Jason M. Bills95820182019-04-22 16:25:34 -0700406static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500407 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700408{
409 static const std::filesystem::path redfishLogDir = "/var/log";
410 static const std::string redfishLogFilename = "redfish";
411
412 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500413 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700414 std::filesystem::directory_iterator(redfishLogDir))
415 {
416 // If we find a redfish log file, save the path
417 std::string filename = dirEnt.path().filename();
418 if (boost::starts_with(filename, redfishLogFilename))
419 {
420 redfishLogFiles.emplace_back(redfishLogDir / filename);
421 }
422 }
423 // As the log files rotate, they are appended with a ".#" that is higher for
424 // the older logs. Since we don't expect more than 10 log files, we
425 // can just sort the list to get them in order from newest to oldest
426 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
427
428 return !redfishLogFiles.empty();
429}
430
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500431inline void getDumpEntryCollection(std::shared_ptr<AsyncResp>& asyncResp,
432 const std::string& dumpType)
433{
434 std::string dumpPath;
435 if (dumpType == "BMC")
436 {
437 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
438 }
439 else if (dumpType == "System")
440 {
441 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
442 }
443 else
444 {
445 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
446 messages::internalError(asyncResp->res);
447 return;
448 }
449
450 crow::connections::systemBus->async_method_call(
451 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
452 GetManagedObjectsType& resp) {
453 if (ec)
454 {
455 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
456 messages::internalError(asyncResp->res);
457 return;
458 }
459
460 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
461 entriesArray = nlohmann::json::array();
462
463 for (auto& object : resp)
464 {
465 bool foundDumpEntry = false;
466 for (auto& interfaceMap : object.second)
467 {
468 if (interfaceMap.first ==
469 ("xyz.openbmc_project.Dump.Entry." + dumpType))
470 {
471 foundDumpEntry = true;
472 break;
473 }
474 }
475
476 if (foundDumpEntry == false)
477 {
478 continue;
479 }
480 std::time_t timestamp;
481 uint64_t size = 0;
482 entriesArray.push_back({});
483 nlohmann::json& thisEntry = entriesArray.back();
484 const std::string& path =
485 static_cast<const std::string&>(object.first);
486 std::size_t lastPos = path.rfind("/");
487 if (lastPos == std::string::npos)
488 {
489 continue;
490 }
491 std::string entryID = path.substr(lastPos + 1);
492
493 for (auto& interfaceMap : object.second)
494 {
495 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
496 {
497
498 for (auto& propertyMap : interfaceMap.second)
499 {
500 if (propertyMap.first == "Size")
501 {
502 auto sizePtr =
503 std::get_if<uint64_t>(&propertyMap.second);
504 if (sizePtr == nullptr)
505 {
506 messages::internalError(asyncResp->res);
507 break;
508 }
509 size = *sizePtr;
510 break;
511 }
512 }
513 }
514 else if (interfaceMap.first ==
515 "xyz.openbmc_project.Time.EpochTime")
516 {
517
518 for (auto& propertyMap : interfaceMap.second)
519 {
520 if (propertyMap.first == "Elapsed")
521 {
522 const uint64_t* usecsTimeStamp =
523 std::get_if<uint64_t>(&propertyMap.second);
524 if (usecsTimeStamp == nullptr)
525 {
526 messages::internalError(asyncResp->res);
527 break;
528 }
529 timestamp =
530 static_cast<std::time_t>(*usecsTimeStamp);
531 break;
532 }
533 }
534 }
535 }
536
537 thisEntry["@odata.type"] = "#LogEntry.v1_5_1.LogEntry";
538 thisEntry["@odata.id"] = dumpPath + entryID;
539 thisEntry["Id"] = entryID;
540 thisEntry["EntryType"] = "Event";
541 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
542 thisEntry["Name"] = dumpType + " Dump Entry";
543
544 thisEntry["Oem"]["OpenBmc"]["@odata.type"] =
545 "#OemLogEntry.v1_0_0.OpenBmc";
546 thisEntry["Oem"]["OpenBmc"]["AdditionalDataSizeBytes"] = size;
547
548 if (dumpType == "BMC")
549 {
550 thisEntry["Oem"]["OpenBmc"]["DiagnosticDataType"] =
551 "Manager";
552 thisEntry["Oem"]["OpenBmc"]["AdditionalDataURI"] =
553 "/redfish/v1/Managers/bmc/LogServices/Dump/"
554 "attachment/" +
555 entryID;
556 }
557 else if (dumpType == "System")
558 {
559 thisEntry["Oem"]["OpenBmc"]["DiagnosticDataType"] = "OEM";
560 thisEntry["Oem"]["OpenBmc"]["OEMDiagnosticDataType"] =
561 "System";
562 thisEntry["Oem"]["OpenBmc"]["AdditionalDataURI"] =
563 "/redfish/v1/Systems/system/LogServices/Dump/"
564 "attachment/" +
565 entryID;
566 }
567 }
568 asyncResp->res.jsonValue["Members@odata.count"] =
569 entriesArray.size();
570 },
571 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
572 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
573}
574
575inline void getDumpEntryById(std::shared_ptr<AsyncResp>& asyncResp,
576 const std::string& entryID,
577 const std::string& dumpType)
578{
579 std::string dumpPath;
580 if (dumpType == "BMC")
581 {
582 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
583 }
584 else if (dumpType == "System")
585 {
586 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
587 }
588 else
589 {
590 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
591 messages::internalError(asyncResp->res);
592 return;
593 }
594
595 crow::connections::systemBus->async_method_call(
596 [asyncResp, entryID, dumpPath, dumpType](
597 const boost::system::error_code ec, GetManagedObjectsType& resp) {
598 if (ec)
599 {
600 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
601 messages::internalError(asyncResp->res);
602 return;
603 }
604
605 for (auto& objectPath : resp)
606 {
607 if (objectPath.first.str.find(
608 "/xyz/openbmc_project/dump/entry/" + entryID) ==
609 std::string::npos)
610 {
611 continue;
612 }
613
614 bool foundDumpEntry = false;
615 for (auto& interfaceMap : objectPath.second)
616 {
617 if (interfaceMap.first ==
618 ("xyz.openbmc_project.Dump.Entry." + dumpType))
619 {
620 foundDumpEntry = true;
621 break;
622 }
623 }
624 if (foundDumpEntry == false)
625 {
626 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
627 messages::internalError(asyncResp->res);
628 return;
629 }
630
631 std::time_t timestamp;
632 uint64_t size = 0;
633
634 for (auto& interfaceMap : objectPath.second)
635 {
636 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
637 {
638 for (auto& propertyMap : interfaceMap.second)
639 {
640 if (propertyMap.first == "Size")
641 {
642 auto sizePtr =
643 std::get_if<uint64_t>(&propertyMap.second);
644 if (sizePtr == nullptr)
645 {
646 messages::internalError(asyncResp->res);
647 break;
648 }
649 size = *sizePtr;
650 break;
651 }
652 }
653 }
654 else if (interfaceMap.first ==
655 "xyz.openbmc_project.Time.EpochTime")
656 {
657 for (auto& propertyMap : interfaceMap.second)
658 {
659 if (propertyMap.first == "Elapsed")
660 {
661 const uint64_t* usecsTimeStamp =
662 std::get_if<uint64_t>(&propertyMap.second);
663 if (usecsTimeStamp == nullptr)
664 {
665 messages::internalError(asyncResp->res);
666 break;
667 }
668 timestamp =
669 static_cast<std::time_t>(*usecsTimeStamp);
670 break;
671 }
672 }
673 }
674 }
675
676 asyncResp->res.jsonValue["@odata.type"] =
677 "#LogEntry.v1_5_1.LogEntry";
678 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
679 asyncResp->res.jsonValue["Id"] = entryID;
680 asyncResp->res.jsonValue["EntryType"] = "Event";
681 asyncResp->res.jsonValue["Created"] =
682 crow::utility::getDateTime(timestamp);
683 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
684
685 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] =
686 "#OemLogEntry.v1_0_0.OpenBmc";
687 asyncResp->res
688 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataSizeBytes"] =
689 size;
690
691 if (dumpType == "BMC")
692 {
693 asyncResp->res
694 .jsonValue["Oem"]["OpenBmc"]["DiagnosticDataType"] =
695 "Manager";
696 asyncResp->res
697 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataURI"] =
698 "/redfish/v1/Managers/bmc/LogServices/Dump/"
699 "attachment/" +
700 entryID;
701 }
702 else if (dumpType == "System")
703 {
704 asyncResp->res
705 .jsonValue["Oem"]["OpenBmc"]["DiagnosticDataType"] =
706 "OEM";
707 asyncResp->res
708 .jsonValue["Oem"]["OpenBmc"]["OEMDiagnosticDataType"] =
709 "System";
710 asyncResp->res
711 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataURI"] =
712 "/redfish/v1/Systems/system/LogServices/Dump/"
713 "attachment/" +
714 entryID;
715 }
716 }
717 },
718 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
719 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
720}
721
722inline void deleteDumpEntry(crow::Response& res, const std::string& entryID)
723{
724 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
725
726 auto respHandler = [asyncResp](const boost::system::error_code ec) {
727 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
728 if (ec)
729 {
730 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
731 << ec;
732 messages::internalError(asyncResp->res);
733 return;
734 }
735 };
736 crow::connections::systemBus->async_method_call(
737 respHandler, "xyz.openbmc_project.Dump.Manager",
738 "/xyz/openbmc_project/dump/entry/" + entryID,
739 "xyz.openbmc_project.Object.Delete", "Delete");
740}
741
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500742inline void createDumpTaskCallback(const crow::Request& req,
743 std::shared_ptr<AsyncResp> asyncResp,
744 const uint32_t& dumpId,
745 const std::string& dumpPath,
746 const std::string& dumpType)
747{
748 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
749 [dumpId, dumpPath, dumpType](
750 boost::system::error_code err, sdbusplus::message::message& m,
751 const std::shared_ptr<task::TaskData>& taskData) {
752 std::vector<std::pair<
753 std::string,
754 std::vector<std::pair<std::string, std::variant<std::string>>>>>
755 interfacesList;
756
757 sdbusplus::message::object_path objPath;
758
759 m.read(objPath, interfacesList);
760
761 for (auto& interface : interfacesList)
762 {
763 if (interface.first ==
764 ("xyz.openbmc_project.Dump.Entry." + dumpType))
765 {
766 nlohmann::json retMessage = messages::success();
767 taskData->messages.emplace_back(retMessage);
768
769 std::string headerLoc =
770 "Location: " + dumpPath + std::to_string(dumpId);
771 taskData->payload->httpHeaders.emplace_back(
772 std::move(headerLoc));
773
774 taskData->state = "Completed";
775 return task::completed;
776 }
777 }
778 return !task::completed;
779 },
780 "type='signal',interface='org.freedesktop.DBus."
781 "ObjectManager',"
782 "member='InterfacesAdded', "
783 "path='/xyz/openbmc_project/dump'");
784
785 task->startTimer(std::chrono::minutes(3));
786 task->populateResp(asyncResp->res);
787 task->payload.emplace(req);
788}
789
790inline void createDump(crow::Response& res, const crow::Request& req,
791 const std::string& dumpType)
792{
793 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
794
795 std::string dumpPath;
796 if (dumpType == "BMC")
797 {
798 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
799 }
800 else if (dumpType == "System")
801 {
802 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
803 }
804 else
805 {
806 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
807 messages::internalError(asyncResp->res);
808 return;
809 }
810
811 std::optional<std::string> diagnosticDataType;
812 std::optional<std::string> oemDiagnosticDataType;
813
814 if (!redfish::json_util::readJson(
815 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
816 "OEMDiagnosticDataType", oemDiagnosticDataType))
817 {
818 return;
819 }
820
821 if (dumpType == "System")
822 {
823 if (!oemDiagnosticDataType || !diagnosticDataType)
824 {
825 BMCWEB_LOG_ERROR << "CreateDump action parameter "
826 "'DiagnosticDataType'/"
827 "'OEMDiagnosticDataType' value not found!";
828 messages::actionParameterMissing(
829 asyncResp->res, "CollectDiagnosticData",
830 "DiagnosticDataType & OEMDiagnosticDataType");
831 return;
832 }
833 else if ((*oemDiagnosticDataType != "System") ||
834 (*diagnosticDataType != "OEM"))
835 {
836 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
837 messages::invalidObject(asyncResp->res,
838 "System Dump creation parameters");
839 return;
840 }
841 }
842 else if (dumpType == "BMC")
843 {
844 if (!diagnosticDataType)
845 {
846 BMCWEB_LOG_ERROR << "CreateDump action parameter "
847 "'DiagnosticDataType' not found!";
848 messages::actionParameterMissing(
849 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
850 return;
851 }
852 else if (*diagnosticDataType != "Manager")
853 {
854 BMCWEB_LOG_ERROR
855 << "Wrong parameter value passed for 'DiagnosticDataType'";
856 messages::invalidObject(asyncResp->res,
857 "BMC Dump creation parameters");
858 return;
859 }
860 }
861
862 crow::connections::systemBus->async_method_call(
863 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
864 const uint32_t& dumpId) {
865 if (ec)
866 {
867 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
868 messages::internalError(asyncResp->res);
869 return;
870 }
871 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
872
873 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
874 },
875 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
876 "xyz.openbmc_project.Dump.Create", "CreateDump");
877}
878
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500879inline void clearDump(crow::Response& res, const std::string& dumpInterface)
880{
881 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
882 crow::connections::systemBus->async_method_call(
883 [asyncResp](const boost::system::error_code ec,
884 const std::vector<std::string>& subTreePaths) {
885 if (ec)
886 {
887 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
888 messages::internalError(asyncResp->res);
889 return;
890 }
891
892 for (const std::string& path : subTreePaths)
893 {
894 std::size_t pos = path.rfind("/");
895 if (pos != std::string::npos)
896 {
897 std::string logID = path.substr(pos + 1);
898 deleteDumpEntry(asyncResp->res, logID);
899 }
900 }
901 },
902 "xyz.openbmc_project.ObjectMapper",
903 "/xyz/openbmc_project/object_mapper",
904 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
905 "/xyz/openbmc_project/dump", 0,
906 std::array<std::string, 1>{dumpInterface});
907}
908
Johnathan Mantey043a0532020-03-10 17:15:28 -0700909static void ParseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500910 const std::vector<std::pair<std::string, VariantType>>& params,
911 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700912{
913 for (auto property : params)
914 {
915 if (property.first == "Timestamp")
916 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500917 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500918 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700919 if (value != nullptr)
920 {
921 timestamp = *value;
922 }
923 }
924 else if (property.first == "Filename")
925 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500926 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500927 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700928 if (value != nullptr)
929 {
930 filename = *value;
931 }
932 }
933 else if (property.first == "Log")
934 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500935 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500936 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700937 if (value != nullptr)
938 {
939 logfile = *value;
940 }
941 }
942 }
943}
944
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500945constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800946class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700947{
948 public:
949 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500950 SystemLogServiceCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800951 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800952 {
953 entityPrivileges = {
954 {boost::beast::http::verb::get, {{"Login"}}},
955 {boost::beast::http::verb::head, {{"Login"}}},
956 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
957 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
958 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
959 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
960 }
961
962 private:
963 /**
964 * Functions triggers appropriate requests on DBus
965 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500966 void doGet(crow::Response& res, const crow::Request& req,
967 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800968 {
969 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800970 // Collections don't include the static data added by SubRoute because
971 // it has a duplicate entry for members
972 asyncResp->res.jsonValue["@odata.type"] =
973 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800974 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800975 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800976 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
977 asyncResp->res.jsonValue["Description"] =
978 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500979 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800980 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800981 logServiceArray.push_back(
982 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500983#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
raviteja-bc9bb6862020-02-03 11:53:32 -0600984 logServiceArray.push_back(
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500985 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600986#endif
987
Jason M. Billsd53dd412019-02-12 17:16:22 -0800988#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
989 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500990 {{"@odata.id",
991 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800992#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800993 asyncResp->res.jsonValue["Members@odata.count"] =
994 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800995
996 crow::connections::systemBus->async_method_call(
997 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500998 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800999 if (ec)
1000 {
1001 BMCWEB_LOG_ERROR << ec;
1002 return;
1003 }
1004
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001005 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001006 {
1007 if (pathStr.find("PostCode") != std::string::npos)
1008 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001009 nlohmann::json& logServiceArray =
ZhikuiRena3316fc2020-01-29 14:58:08 -08001010 asyncResp->res.jsonValue["Members"];
1011 logServiceArray.push_back(
1012 {{"@odata.id", "/redfish/v1/Systems/system/"
1013 "LogServices/PostCodes"}});
1014 asyncResp->res.jsonValue["Members@odata.count"] =
1015 logServiceArray.size();
1016 return;
1017 }
1018 }
1019 },
1020 "xyz.openbmc_project.ObjectMapper",
1021 "/xyz/openbmc_project/object_mapper",
1022 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001023 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001024 }
1025};
1026
1027class EventLogService : public Node
1028{
1029 public:
1030 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001031 EventLogService(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001032 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001033 {
1034 entityPrivileges = {
1035 {boost::beast::http::verb::get, {{"Login"}}},
1036 {boost::beast::http::verb::head, {{"Login"}}},
1037 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1038 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1039 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1040 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1041 }
1042
1043 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001044 void doGet(crow::Response& res, const crow::Request& req,
1045 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001046 {
1047 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1048
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001049 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001050 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001051 asyncResp->res.jsonValue["@odata.type"] =
1052 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001053 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1054 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -05001055 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001056 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1057 asyncResp->res.jsonValue["Entries"] = {
1058 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -08001059 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -05001060 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1061
1062 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1063 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -07001064 }
1065};
1066
Tim Lee1f56a3a2019-10-09 10:17:57 +08001067class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -07001068{
1069 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001070 JournalEventLogClear(CrowApp& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -07001071 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1072 "LogService.ClearLog/")
1073 {
1074 entityPrivileges = {
1075 {boost::beast::http::verb::get, {{"Login"}}},
1076 {boost::beast::http::verb::head, {{"Login"}}},
1077 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1078 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1079 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1080 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1081 }
1082
1083 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001084 void doPost(crow::Response& res, const crow::Request& req,
1085 const std::vector<std::string>& params) override
Jason M. Bills489640c2019-05-17 09:56:36 -07001086 {
1087 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1088
1089 // Clear the EventLog by deleting the log files
1090 std::vector<std::filesystem::path> redfishLogFiles;
1091 if (getRedfishLogFiles(redfishLogFiles))
1092 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001093 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -07001094 {
1095 std::error_code ec;
1096 std::filesystem::remove(file, ec);
1097 }
1098 }
1099
1100 // Reload rsyslog so it knows to start new log files
1101 crow::connections::systemBus->async_method_call(
1102 [asyncResp](const boost::system::error_code ec) {
1103 if (ec)
1104 {
1105 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1106 messages::internalError(asyncResp->res);
1107 return;
1108 }
1109
1110 messages::success(asyncResp->res);
1111 },
1112 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1113 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1114 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001115 }
1116};
1117
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001118static int fillEventLogEntryJson(const std::string& logEntryID,
Jason M. Bills95820182019-04-22 16:25:34 -07001119 const std::string logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001120 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001121{
Jason M. Bills95820182019-04-22 16:25:34 -07001122 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001123 // First get the Timestamp
1124 size_t space = logEntry.find_first_of(" ");
1125 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001126 {
1127 return 1;
1128 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001129 std::string timestamp = logEntry.substr(0, space);
1130 // Then get the log contents
1131 size_t entryStart = logEntry.find_first_not_of(" ", space);
1132 if (entryStart == std::string::npos)
1133 {
1134 return 1;
1135 }
1136 std::string_view entry(logEntry);
1137 entry.remove_prefix(entryStart);
1138 // Use split to separate the entry into its fields
1139 std::vector<std::string> logEntryFields;
1140 boost::split(logEntryFields, entry, boost::is_any_of(","),
1141 boost::token_compress_on);
1142 // We need at least a MessageId to be valid
1143 if (logEntryFields.size() < 1)
1144 {
1145 return 1;
1146 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001147 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001148
Jason M. Bills4851d452019-03-28 11:27:48 -07001149 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001150 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001151 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001152
Jason M. Bills4851d452019-03-28 11:27:48 -07001153 std::string msg;
1154 std::string severity;
1155 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001156 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001157 msg = message->message;
1158 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001159 }
1160
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001161 // Get the MessageArgs from the log if there are any
1162 boost::beast::span<std::string> messageArgs;
1163 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001164 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001165 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001166 // If the first string is empty, assume there are no MessageArgs
1167 std::size_t messageArgsSize = 0;
1168 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001169 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001170 messageArgsSize = logEntryFields.size() - 1;
1171 }
1172
1173 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
1174
1175 // Fill the MessageArgs into the Message
1176 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001177 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001178 {
1179 std::string argStr = "%" + std::to_string(++i);
1180 size_t argPos = msg.find(argStr);
1181 if (argPos != std::string::npos)
1182 {
1183 msg.replace(argPos, argStr.length(), messageArg);
1184 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001185 }
1186 }
1187
Jason M. Bills95820182019-04-22 16:25:34 -07001188 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1189 // format which matches the Redfish format except for the fractional seconds
1190 // between the '.' and the '+', so just remove them.
1191 std::size_t dot = timestamp.find_first_of(".");
1192 std::size_t plus = timestamp.find_first_of("+");
1193 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001194 {
Jason M. Bills95820182019-04-22 16:25:34 -07001195 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001196 }
1197
1198 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001199 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001200 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001201 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001202 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001203 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001204 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001205 {"Id", logEntryID},
1206 {"Message", std::move(msg)},
1207 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001208 {"MessageArgs", std::move(messageArgs)},
1209 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001210 {"Severity", std::move(severity)},
1211 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001212 return 0;
1213}
1214
Anthony Wilson27062602019-04-22 02:10:09 -05001215class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001216{
1217 public:
1218 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001219 JournalEventLogEntryCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001220 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001221 {
1222 entityPrivileges = {
1223 {boost::beast::http::verb::get, {{"Login"}}},
1224 {boost::beast::http::verb::head, {{"Login"}}},
1225 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1226 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1227 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1228 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1229 }
1230
1231 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001232 void doGet(crow::Response& res, const crow::Request& req,
1233 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001234 {
1235 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -07001236 uint64_t skip = 0;
1237 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001238 if (!getSkipParam(asyncResp->res, req, skip))
1239 {
1240 return;
1241 }
1242 if (!getTopParam(asyncResp->res, req, top))
1243 {
1244 return;
1245 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001246 // Collections don't include the static data added by SubRoute because
1247 // it has a duplicate entry for members
1248 asyncResp->res.jsonValue["@odata.type"] =
1249 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001250 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001251 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001252 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1253 asyncResp->res.jsonValue["Description"] =
1254 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -07001255
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001256 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001257 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -07001258 // Go through the log files and create a unique ID for each entry
1259 std::vector<std::filesystem::path> redfishLogFiles;
1260 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +00001261 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -07001262 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -07001263
1264 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -07001265 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1266 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001267 {
Jason M. Billscd225da2019-05-08 15:31:57 -07001268 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -07001269 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001270 {
1271 continue;
1272 }
1273
Jason M. Billse85d6b12019-07-29 17:01:15 -07001274 // Reset the unique ID on the first entry
1275 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -07001276 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001277 {
Jason M. Bills95820182019-04-22 16:25:34 -07001278 entryCount++;
1279 // Handle paging using skip (number of entries to skip from the
1280 // start) and top (number of entries to display)
1281 if (entryCount <= skip || entryCount > skip + top)
1282 {
1283 continue;
1284 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001285
Jason M. Bills95820182019-04-22 16:25:34 -07001286 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001287 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -07001288 {
1289 continue;
1290 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001291
Jason M. Billse85d6b12019-07-29 17:01:15 -07001292 if (firstEntry)
1293 {
1294 firstEntry = false;
1295 }
1296
Jason M. Bills95820182019-04-22 16:25:34 -07001297 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001298 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -07001299 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1300 {
1301 messages::internalError(asyncResp->res);
1302 return;
1303 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001304 }
1305 }
1306 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1307 if (skip + top < entryCount)
1308 {
1309 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -07001310 "/redfish/v1/Systems/system/LogServices/EventLog/"
1311 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001312 std::to_string(skip + top);
1313 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001314 }
1315};
1316
Jason M. Bills897967d2019-07-29 17:05:30 -07001317class JournalEventLogEntry : public Node
1318{
1319 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001320 JournalEventLogEntry(CrowApp& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -07001321 Node(app,
1322 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1323 std::string())
1324 {
1325 entityPrivileges = {
1326 {boost::beast::http::verb::get, {{"Login"}}},
1327 {boost::beast::http::verb::head, {{"Login"}}},
1328 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1329 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1330 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1331 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1332 }
1333
1334 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001335 void doGet(crow::Response& res, const crow::Request& req,
1336 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -07001337 {
1338 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1339 if (params.size() != 1)
1340 {
1341 messages::internalError(asyncResp->res);
1342 return;
1343 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001344 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -07001345
1346 // Go through the log files and check the unique ID for each entry to
1347 // find the target entry
1348 std::vector<std::filesystem::path> redfishLogFiles;
1349 getRedfishLogFiles(redfishLogFiles);
1350 std::string logEntry;
1351
1352 // Oldest logs are in the last file, so start there and loop backwards
1353 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1354 it++)
1355 {
1356 std::ifstream logStream(*it);
1357 if (!logStream.is_open())
1358 {
1359 continue;
1360 }
1361
1362 // Reset the unique ID on the first entry
1363 bool firstEntry = true;
1364 while (std::getline(logStream, logEntry))
1365 {
1366 std::string idStr;
1367 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1368 {
1369 continue;
1370 }
1371
1372 if (firstEntry)
1373 {
1374 firstEntry = false;
1375 }
1376
1377 if (idStr == targetID)
1378 {
1379 if (fillEventLogEntryJson(idStr, logEntry,
1380 asyncResp->res.jsonValue) != 0)
1381 {
1382 messages::internalError(asyncResp->res);
1383 return;
1384 }
1385 return;
1386 }
1387 }
1388 }
1389 // Requested ID was not found
1390 messages::resourceMissingAtURI(asyncResp->res, targetID);
1391 }
1392};
1393
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001394class DBusEventLogEntryCollection : public Node
1395{
1396 public:
1397 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001398 DBusEventLogEntryCollection(CrowApp& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001399 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
1400 {
1401 entityPrivileges = {
1402 {boost::beast::http::verb::get, {{"Login"}}},
1403 {boost::beast::http::verb::head, {{"Login"}}},
1404 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1405 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1406 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1407 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1408 }
1409
1410 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001411 void doGet(crow::Response& res, const crow::Request& req,
1412 const std::vector<std::string>& params) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001413 {
1414 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1415
1416 // Collections don't include the static data added by SubRoute because
1417 // it has a duplicate entry for members
1418 asyncResp->res.jsonValue["@odata.type"] =
1419 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001420 asyncResp->res.jsonValue["@odata.id"] =
1421 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1422 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1423 asyncResp->res.jsonValue["Description"] =
1424 "Collection of System Event Log Entries";
1425
Andrew Geisslercb92c032018-08-17 07:56:14 -07001426 // DBus implementation of EventLog/Entries
1427 // Make call to Logging Service to find all log entry objects
1428 crow::connections::systemBus->async_method_call(
1429 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001430 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001431 if (ec)
1432 {
1433 // TODO Handle for specific error code
1434 BMCWEB_LOG_ERROR
1435 << "getLogEntriesIfaceData resp_handler got error "
1436 << ec;
1437 messages::internalError(asyncResp->res);
1438 return;
1439 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001440 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001441 asyncResp->res.jsonValue["Members"];
1442 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001443 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001444 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001445 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001446 {
1447 if (interfaceMap.first !=
1448 "xyz.openbmc_project.Logging.Entry")
1449 {
1450 BMCWEB_LOG_DEBUG << "Bailing early on "
1451 << interfaceMap.first;
1452 continue;
1453 }
1454 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001455 nlohmann::json& thisEntry = entriesArray.back();
1456 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001457 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001458 std::string* severity = nullptr;
1459 std::string* message = nullptr;
1460 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001461 {
1462 if (propertyMap.first == "Id")
1463 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001464 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001465 if (id == nullptr)
1466 {
1467 messages::propertyMissing(asyncResp->res,
1468 "Id");
1469 }
1470 }
1471 else if (propertyMap.first == "Timestamp")
1472 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001473 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001474 std::get_if<uint64_t>(&propertyMap.second);
1475 if (millisTimeStamp == nullptr)
1476 {
1477 messages::propertyMissing(asyncResp->res,
1478 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001479 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001480 }
1481 // Retrieve Created property with format:
1482 // yyyy-mm-ddThh:mm:ss
1483 std::chrono::milliseconds chronoTimeStamp(
1484 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001485 timestamp = std::chrono::duration_cast<
1486 std::chrono::duration<int>>(
1487 chronoTimeStamp)
1488 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001489 }
1490 else if (propertyMap.first == "Severity")
1491 {
1492 severity = std::get_if<std::string>(
1493 &propertyMap.second);
1494 if (severity == nullptr)
1495 {
1496 messages::propertyMissing(asyncResp->res,
1497 "Severity");
1498 }
1499 }
1500 else if (propertyMap.first == "Message")
1501 {
1502 message = std::get_if<std::string>(
1503 &propertyMap.second);
1504 if (message == nullptr)
1505 {
1506 messages::propertyMissing(asyncResp->res,
1507 "Message");
1508 }
1509 }
1510 }
1511 thisEntry = {
1512 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001513 {"@odata.id",
1514 "/redfish/v1/Systems/system/LogServices/EventLog/"
1515 "Entries/" +
1516 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001517 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001518 {"Id", std::to_string(*id)},
1519 {"Message", *message},
1520 {"EntryType", "Event"},
1521 {"Severity",
1522 translateSeverityDbusToRedfish(*severity)},
1523 {"Created", crow::utility::getDateTime(timestamp)}};
1524 }
1525 }
1526 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001527 [](const nlohmann::json& left,
1528 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001529 return (left["Id"] <= right["Id"]);
1530 });
1531 asyncResp->res.jsonValue["Members@odata.count"] =
1532 entriesArray.size();
1533 },
1534 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1535 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001536 }
1537};
1538
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001539class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001540{
1541 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001542 DBusEventLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001543 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001544 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1545 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001546 {
1547 entityPrivileges = {
1548 {boost::beast::http::verb::get, {{"Login"}}},
1549 {boost::beast::http::verb::head, {{"Login"}}},
1550 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1551 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1552 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1553 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1554 }
1555
1556 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001557 void doGet(crow::Response& res, const crow::Request& req,
1558 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001559 {
1560 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001561 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001562 {
1563 messages::internalError(asyncResp->res);
1564 return;
1565 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001566 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001567
Andrew Geisslercb92c032018-08-17 07:56:14 -07001568 // DBus implementation of EventLog/Entries
1569 // Make call to Logging Service to find all log entry objects
1570 crow::connections::systemBus->async_method_call(
1571 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001572 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001573 if (ec)
1574 {
1575 BMCWEB_LOG_ERROR
1576 << "EventLogEntry (DBus) resp_handler got error " << ec;
1577 messages::internalError(asyncResp->res);
1578 return;
1579 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001580 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001581 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001582 std::string* severity = nullptr;
1583 std::string* message = nullptr;
1584 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001585 {
1586 if (propertyMap.first == "Id")
1587 {
1588 id = std::get_if<uint32_t>(&propertyMap.second);
1589 if (id == nullptr)
1590 {
1591 messages::propertyMissing(asyncResp->res, "Id");
1592 }
1593 }
1594 else if (propertyMap.first == "Timestamp")
1595 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001596 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001597 std::get_if<uint64_t>(&propertyMap.second);
1598 if (millisTimeStamp == nullptr)
1599 {
1600 messages::propertyMissing(asyncResp->res,
1601 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001602 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001603 }
1604 // Retrieve Created property with format:
1605 // yyyy-mm-ddThh:mm:ss
1606 std::chrono::milliseconds chronoTimeStamp(
1607 *millisTimeStamp);
1608 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001609 std::chrono::duration_cast<
1610 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001611 .count();
1612 }
1613 else if (propertyMap.first == "Severity")
1614 {
1615 severity =
1616 std::get_if<std::string>(&propertyMap.second);
1617 if (severity == nullptr)
1618 {
1619 messages::propertyMissing(asyncResp->res,
1620 "Severity");
1621 }
1622 }
1623 else if (propertyMap.first == "Message")
1624 {
1625 message = std::get_if<std::string>(&propertyMap.second);
1626 if (message == nullptr)
1627 {
1628 messages::propertyMissing(asyncResp->res,
1629 "Message");
1630 }
1631 }
1632 }
Ed Tanous271584a2019-07-09 16:24:22 -07001633 if (id == nullptr || message == nullptr || severity == nullptr)
1634 {
1635 return;
1636 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001637 asyncResp->res.jsonValue = {
1638 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001639 {"@odata.id",
1640 "/redfish/v1/Systems/system/LogServices/EventLog/"
1641 "Entries/" +
1642 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001643 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001644 {"Id", std::to_string(*id)},
1645 {"Message", *message},
1646 {"EntryType", "Event"},
1647 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001648 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001649 },
1650 "xyz.openbmc_project.Logging",
1651 "/xyz/openbmc_project/logging/entry/" + entryID,
1652 "org.freedesktop.DBus.Properties", "GetAll",
1653 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001654 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001655
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001656 void doDelete(crow::Response& res, const crow::Request& req,
1657 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001658 {
1659
1660 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1661
1662 auto asyncResp = std::make_shared<AsyncResp>(res);
1663
1664 if (params.size() != 1)
1665 {
1666 messages::internalError(asyncResp->res);
1667 return;
1668 }
1669 std::string entryID = params[0];
1670
1671 dbus::utility::escapePathForDbus(entryID);
1672
1673 // Process response from Logging service.
1674 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1675 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1676 if (ec)
1677 {
1678 // TODO Handle for specific error code
1679 BMCWEB_LOG_ERROR
1680 << "EventLogEntry (DBus) doDelete respHandler got error "
1681 << ec;
1682 asyncResp->res.result(
1683 boost::beast::http::status::internal_server_error);
1684 return;
1685 }
1686
1687 asyncResp->res.result(boost::beast::http::status::ok);
1688 };
1689
1690 // Make call to Logging service to request Delete Log
1691 crow::connections::systemBus->async_method_call(
1692 respHandler, "xyz.openbmc_project.Logging",
1693 "/xyz/openbmc_project/logging/entry/" + entryID,
1694 "xyz.openbmc_project.Object.Delete", "Delete");
1695 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001696};
1697
1698class BMCLogServiceCollection : public Node
1699{
1700 public:
1701 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001702 BMCLogServiceCollection(CrowApp& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001703 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001704 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001705 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001706 {boost::beast::http::verb::get, {{"Login"}}},
1707 {boost::beast::http::verb::head, {{"Login"}}},
1708 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1709 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1710 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1711 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001712 }
1713
1714 private:
1715 /**
1716 * Functions triggers appropriate requests on DBus
1717 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001718 void doGet(crow::Response& res, const crow::Request& req,
1719 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001720 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001721 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001722 // Collections don't include the static data added by SubRoute because
1723 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001724 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001725 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001726 asyncResp->res.jsonValue["@odata.id"] =
1727 "/redfish/v1/Managers/bmc/LogServices";
1728 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1729 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001730 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001731 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001732 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001733#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1734 logServiceArray.push_back(
1735 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1736#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001737#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1738 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001739 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001740#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001741 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001742 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001743 }
1744};
1745
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001746class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001747{
1748 public:
1749 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001750 BMCJournalLogService(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001751 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001752 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001753 entityPrivileges = {
1754 {boost::beast::http::verb::get, {{"Login"}}},
1755 {boost::beast::http::verb::head, {{"Login"}}},
1756 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1757 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1758 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1759 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1760 }
1761
1762 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001763 void doGet(crow::Response& res, const crow::Request& req,
1764 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001765 {
1766 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001767 asyncResp->res.jsonValue["@odata.type"] =
1768 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001769 asyncResp->res.jsonValue["@odata.id"] =
1770 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001771 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1772 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1773 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001774 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001775 asyncResp->res.jsonValue["Entries"] = {
1776 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001777 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001778 }
1779};
1780
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001781static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1782 sd_journal* journal,
1783 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001784{
1785 // Get the Log Entry contents
1786 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001787
Ed Tanous39e77502019-03-04 17:35:53 -08001788 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001789 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001790 if (ret < 0)
1791 {
1792 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1793 return 1;
1794 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001795
1796 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001797 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001798 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001799 if (ret < 0)
1800 {
1801 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001802 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001803
1804 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001805 std::string entryTimeStr;
1806 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001807 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001808 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001809 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001810
1811 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001812 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001813 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001814 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1815 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001816 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001817 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001818 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001819 {"EntryType", "Oem"},
1820 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001821 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001822 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001823 {"Created", std::move(entryTimeStr)}};
1824 return 0;
1825}
1826
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001827class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001828{
1829 public:
1830 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001831 BMCJournalLogEntryCollection(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001832 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001833 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001834 entityPrivileges = {
1835 {boost::beast::http::verb::get, {{"Login"}}},
1836 {boost::beast::http::verb::head, {{"Login"}}},
1837 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1838 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1839 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1840 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1841 }
1842
1843 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001844 void doGet(crow::Response& res, const crow::Request& req,
1845 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001846 {
1847 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001848 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001849 uint64_t skip = 0;
1850 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001851 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001852 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001853 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001854 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001855 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001856 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001857 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001858 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001859 // Collections don't include the static data added by SubRoute because
1860 // it has a duplicate entry for members
1861 asyncResp->res.jsonValue["@odata.type"] =
1862 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001863 asyncResp->res.jsonValue["@odata.id"] =
1864 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001865 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001866 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001867 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1868 asyncResp->res.jsonValue["Description"] =
1869 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001870 asyncResp->res.jsonValue["@odata.id"] =
1871 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001872 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001873 logEntryArray = nlohmann::json::array();
1874
1875 // Go through the journal and use the timestamp to create a unique ID
1876 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001877 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001878 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1879 if (ret < 0)
1880 {
1881 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001882 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001883 return;
1884 }
1885 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1886 journalTmp, sd_journal_close);
1887 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001888 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001889 // Reset the unique ID on the first entry
1890 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001891 SD_JOURNAL_FOREACH(journal.get())
1892 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001893 entryCount++;
1894 // Handle paging using skip (number of entries to skip from the
1895 // start) and top (number of entries to display)
1896 if (entryCount <= skip || entryCount > skip + top)
1897 {
1898 continue;
1899 }
1900
Jason M. Bills16428a12018-11-02 12:42:29 -07001901 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001902 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001903 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001904 continue;
1905 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001906
Jason M. Billse85d6b12019-07-29 17:01:15 -07001907 if (firstEntry)
1908 {
1909 firstEntry = false;
1910 }
1911
Jason M. Billse1f26342018-07-18 12:12:00 -07001912 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001913 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001914 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1915 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001916 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001917 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001918 return;
1919 }
1920 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001921 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1922 if (skip + top < entryCount)
1923 {
1924 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001925 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001926 std::to_string(skip + top);
1927 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001928 }
1929};
1930
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001931class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001932{
1933 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001934 BMCJournalLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001935 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001936 std::string())
1937 {
1938 entityPrivileges = {
1939 {boost::beast::http::verb::get, {{"Login"}}},
1940 {boost::beast::http::verb::head, {{"Login"}}},
1941 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1942 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1943 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1944 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1945 }
1946
1947 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001948 void doGet(crow::Response& res, const crow::Request& req,
1949 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001950 {
1951 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1952 if (params.size() != 1)
1953 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001954 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001955 return;
1956 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001957 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001958 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001959 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001960 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001961 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001962 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001963 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001964 }
1965
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001966 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001967 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1968 if (ret < 0)
1969 {
1970 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001971 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001972 return;
1973 }
1974 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1975 journalTmp, sd_journal_close);
1976 journalTmp = nullptr;
1977 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001978 // tracking the unique ID
1979 std::string idStr;
1980 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001981 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301982 if (ret < 0)
1983 {
1984 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1985 << strerror(-ret);
1986 messages::internalError(asyncResp->res);
1987 return;
1988 }
Ed Tanous271584a2019-07-09 16:24:22 -07001989 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001990 {
1991 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001992 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1993 {
1994 messages::internalError(asyncResp->res);
1995 return;
1996 }
1997 if (firstEntry)
1998 {
1999 firstEntry = false;
2000 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002001 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002002 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07002003 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002004 {
2005 messages::resourceMissingAtURI(asyncResp->res, entryID);
2006 return;
2007 }
2008
2009 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2010 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07002011 {
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 }
2016};
2017
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002018class BMCDumpService : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002019{
2020 public:
2021 template <typename CrowApp>
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002022 BMCDumpService(CrowApp& app) :
2023 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002024 {
2025 entityPrivileges = {
2026 {boost::beast::http::verb::get, {{"Login"}}},
2027 {boost::beast::http::verb::head, {{"Login"}}},
2028 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2029 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2030 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2031 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2032 }
2033
2034 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002035 void doGet(crow::Response& res, const crow::Request& req,
2036 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002037 {
2038 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2039
2040 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002041 "/redfish/v1/Managers/bmc/LogServices/Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002042 asyncResp->res.jsonValue["@odata.type"] =
2043 "#LogService.v1_1_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002044 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2045 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2046 asyncResp->res.jsonValue["Id"] = "Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002047 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
raviteja-bc9bb6862020-02-03 11:53:32 -06002048 asyncResp->res.jsonValue["Entries"] = {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002049 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2050 asyncResp->res.jsonValue["Actions"] = {
2051 {"#LogService.ClearLog",
2052 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2053 "Actions/LogService.ClearLog"}}},
2054 {"Oem",
2055 {{"#OemLogService.CollectDiagnosticData",
2056 {{"target",
2057 "/redfish/v1/Managers/bmc/LogServices/Dump/"
2058 "Actions/Oem/OemLogService.CollectDiagnosticData"}}}}}};
raviteja-bc9bb6862020-02-03 11:53:32 -06002059 }
2060};
2061
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002062class BMCDumpEntryCollection : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002063{
2064 public:
2065 template <typename CrowApp>
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002066 BMCDumpEntryCollection(CrowApp& app) :
2067 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002068 {
2069 entityPrivileges = {
2070 {boost::beast::http::verb::get, {{"Login"}}},
2071 {boost::beast::http::verb::head, {{"Login"}}},
2072 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2073 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2074 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2075 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2076 }
2077
2078 private:
2079 /**
2080 * Functions triggers appropriate requests on DBus
2081 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002082 void doGet(crow::Response& res, const crow::Request& req,
2083 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002084 {
2085 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2086
2087 asyncResp->res.jsonValue["@odata.type"] =
2088 "#LogEntryCollection.LogEntryCollection";
2089 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002090 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2091 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002092 asyncResp->res.jsonValue["Description"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002093 "Collection of BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002094
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002095 getDumpEntryCollection(asyncResp, "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002096 }
2097};
2098
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002099class BMCDumpEntry : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002100{
2101 public:
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002102 BMCDumpEntry(CrowApp& app) :
2103 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/",
raviteja-bc9bb6862020-02-03 11:53:32 -06002104 std::string())
2105 {
2106 entityPrivileges = {
2107 {boost::beast::http::verb::get, {{"Login"}}},
2108 {boost::beast::http::verb::head, {{"Login"}}},
2109 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2110 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2111 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2112 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2113 }
2114
2115 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002116 void doGet(crow::Response& res, const crow::Request& req,
2117 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002118 {
2119 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2120 if (params.size() != 1)
2121 {
2122 messages::internalError(asyncResp->res);
2123 return;
2124 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002125 getDumpEntryById(asyncResp, params[0], "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002126 }
2127
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002128 void doDelete(crow::Response& res, const crow::Request& req,
2129 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002130 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002131 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002132 if (params.size() != 1)
2133 {
2134 messages::internalError(asyncResp->res);
2135 return;
2136 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002137 deleteDumpEntry(asyncResp->res, params[0]);
2138 }
2139};
raviteja-bc9bb6862020-02-03 11:53:32 -06002140
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002141class BMCDumpCreate : public Node
2142{
2143 public:
2144 BMCDumpCreate(CrowApp& app) :
2145 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2146 "Actions/Oem/"
2147 "OemLogService.CollectDiagnosticData/")
2148 {
2149 entityPrivileges = {
2150 {boost::beast::http::verb::get, {{"Login"}}},
2151 {boost::beast::http::verb::head, {{"Login"}}},
2152 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2153 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2154 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2155 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2156 }
2157
2158 private:
2159 void doPost(crow::Response& res, const crow::Request& req,
2160 const std::vector<std::string>& params) override
2161 {
2162 createDump(res, req, "BMC");
2163 }
2164};
2165
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002166class BMCDumpClear : public Node
2167{
2168 public:
2169 BMCDumpClear(CrowApp& app) :
2170 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2171 "Actions/"
2172 "LogService.ClearLog/")
2173 {
2174 entityPrivileges = {
2175 {boost::beast::http::verb::get, {{"Login"}}},
2176 {boost::beast::http::verb::head, {{"Login"}}},
2177 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2178 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2179 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2180 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2181 }
2182
2183 private:
2184 void doPost(crow::Response& res, const crow::Request& req,
2185 const std::vector<std::string>& params) override
2186 {
2187 clearDump(res, "xyz.openbmc_project.Dump.Entry.BMC");
2188 }
2189};
2190
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002191class SystemDumpService : public Node
2192{
2193 public:
2194 template <typename CrowApp>
2195 SystemDumpService(CrowApp& app) :
2196 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/")
2197 {
2198 entityPrivileges = {
2199 {boost::beast::http::verb::get, {{"Login"}}},
2200 {boost::beast::http::verb::head, {{"Login"}}},
2201 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2202 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2203 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2204 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2205 }
raviteja-bc9bb6862020-02-03 11:53:32 -06002206
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002207 private:
2208 void doGet(crow::Response& res, const crow::Request& req,
2209 const std::vector<std::string>& params) override
2210 {
2211 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002212
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002213 asyncResp->res.jsonValue["@odata.id"] =
2214 "/redfish/v1/Systems/system/LogServices/Dump";
2215 asyncResp->res.jsonValue["@odata.type"] =
2216 "#LogService.v1_1_0.LogService";
2217 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2218 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2219 asyncResp->res.jsonValue["Id"] = "Dump";
2220 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2221 asyncResp->res.jsonValue["Entries"] = {
2222 {"@odata.id",
2223 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2224 asyncResp->res.jsonValue["Actions"] = {
2225 {"#LogService.ClearLog",
2226 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2227 "LogService.ClearLog"}}},
2228 {"Oem",
2229 {{"#OemLogService.CollectDiagnosticData",
2230 {{"target",
2231 "/redfish/v1/Systems/system/LogServices/Dump/Actions/Oem/"
2232 "OemLogService.CollectDiagnosticData"}}}}}};
2233 }
2234};
2235
2236class SystemDumpEntryCollection : public Node
2237{
2238 public:
2239 template <typename CrowApp>
2240 SystemDumpEntryCollection(CrowApp& app) :
2241 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
2242 {
2243 entityPrivileges = {
2244 {boost::beast::http::verb::get, {{"Login"}}},
2245 {boost::beast::http::verb::head, {{"Login"}}},
2246 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2247 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2248 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2249 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2250 }
2251
2252 private:
2253 /**
2254 * Functions triggers appropriate requests on DBus
2255 */
2256 void doGet(crow::Response& res, const crow::Request& req,
2257 const std::vector<std::string>& params) override
2258 {
2259 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2260
2261 asyncResp->res.jsonValue["@odata.type"] =
2262 "#LogEntryCollection.LogEntryCollection";
2263 asyncResp->res.jsonValue["@odata.id"] =
2264 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2265 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2266 asyncResp->res.jsonValue["Description"] =
2267 "Collection of System Dump Entries";
2268
2269 getDumpEntryCollection(asyncResp, "System");
2270 }
2271};
2272
2273class SystemDumpEntry : public Node
2274{
2275 public:
2276 SystemDumpEntry(CrowApp& app) :
2277 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/",
2278 std::string())
2279 {
2280 entityPrivileges = {
2281 {boost::beast::http::verb::get, {{"Login"}}},
2282 {boost::beast::http::verb::head, {{"Login"}}},
2283 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2284 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2285 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2286 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2287 }
2288
2289 private:
2290 void doGet(crow::Response& res, const crow::Request& req,
2291 const std::vector<std::string>& params) override
2292 {
2293 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2294 if (params.size() != 1)
2295 {
2296 messages::internalError(asyncResp->res);
2297 return;
2298 }
2299 getDumpEntryById(asyncResp, params[0], "System");
2300 }
2301
2302 void doDelete(crow::Response& res, const crow::Request& req,
2303 const std::vector<std::string>& params) override
2304 {
2305 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2306 if (params.size() != 1)
2307 {
2308 messages::internalError(asyncResp->res);
2309 return;
2310 }
2311 deleteDumpEntry(asyncResp->res, params[0]);
raviteja-bc9bb6862020-02-03 11:53:32 -06002312 }
2313};
2314
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002315class SystemDumpCreate : public Node
2316{
2317 public:
2318 SystemDumpCreate(CrowApp& app) :
2319 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2320 "Actions/Oem/"
2321 "OemLogService.CollectDiagnosticData/")
2322 {
2323 entityPrivileges = {
2324 {boost::beast::http::verb::get, {{"Login"}}},
2325 {boost::beast::http::verb::head, {{"Login"}}},
2326 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2327 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2328 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2329 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2330 }
2331
2332 private:
2333 void doPost(crow::Response& res, const crow::Request& req,
2334 const std::vector<std::string>& params) override
2335 {
2336 createDump(res, req, "System");
2337 }
2338};
2339
raviteja-b06578432020-02-03 12:50:42 -06002340class SystemDumpEntryDownload : public Node
2341{
2342 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002343 SystemDumpEntryDownload(CrowApp& app) :
raviteja-b06578432020-02-03 12:50:42 -06002344 Node(app,
2345 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/"
2346 "Actions/"
2347 "LogEntry.DownloadLog/",
2348 std::string())
2349 {
2350 entityPrivileges = {
2351 {boost::beast::http::verb::get, {{"Login"}}},
2352 {boost::beast::http::verb::head, {{"Login"}}},
2353 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2354 }
2355
2356 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002357 void doPost(crow::Response& res, const crow::Request& req,
2358 const std::vector<std::string>& params) override
raviteja-b06578432020-02-03 12:50:42 -06002359 {
2360 if (params.size() != 1)
2361 {
2362 messages::internalError(res);
2363 return;
2364 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002365 const std::string& entryID = params[0];
raviteja-b06578432020-02-03 12:50:42 -06002366 crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID);
2367 }
2368};
2369
raviteja-b013487e2020-03-03 03:20:48 -06002370class SystemDumpClear : public Node
2371{
2372 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002373 SystemDumpClear(CrowApp& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002374 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
raviteja-b013487e2020-03-03 03:20:48 -06002375 "Actions/"
2376 "LogService.ClearLog/")
2377 {
2378 entityPrivileges = {
2379 {boost::beast::http::verb::get, {{"Login"}}},
2380 {boost::beast::http::verb::head, {{"Login"}}},
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002381 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2382 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2383 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
raviteja-b013487e2020-03-03 03:20:48 -06002384 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2385 }
2386
2387 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002388 void doPost(crow::Response& res, const crow::Request& req,
2389 const std::vector<std::string>& params) override
raviteja-b013487e2020-03-03 03:20:48 -06002390 {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002391 clearDump(res, "xyz.openbmc_project.Dump.Entry.System");
raviteja-b013487e2020-03-03 03:20:48 -06002392 }
2393};
2394
Jason M. Bills424c4172019-03-21 13:50:33 -07002395class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07002396{
2397 public:
2398 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002399 CrashdumpService(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002400 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002401 {
AppaRao Puli39460282020-04-07 17:03:04 +05302402 // Note: Deviated from redfish privilege registry for GET & HEAD
2403 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002404 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302405 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2406 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002407 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2408 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2409 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2410 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002411 }
2412
2413 private:
2414 /**
2415 * Functions triggers appropriate requests on DBus
2416 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002417 void doGet(crow::Response& res, const crow::Request& req,
2418 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002419 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002420 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002421 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08002422 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002423 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002424 asyncResp->res.jsonValue["@odata.type"] =
2425 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06002426 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2427 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2428 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002429 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2430 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08002431 asyncResp->res.jsonValue["Entries"] = {
2432 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07002433 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07002434 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002435 {"#LogService.ClearLog",
2436 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2437 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07002438 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07002439 {{"#Crashdump.OnDemand",
2440 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002441 "Actions/Oem/Crashdump.OnDemand"}}},
2442 {"#Crashdump.Telemetry",
2443 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2444 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002445
2446#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07002447 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07002448 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05002449 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2450 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07002451#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07002452 }
2453};
2454
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002455class CrashdumpClear : public Node
2456{
2457 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002458 CrashdumpClear(CrowApp& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002459 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2460 "LogService.ClearLog/")
2461 {
AppaRao Puli39460282020-04-07 17:03:04 +05302462 // Note: Deviated from redfish privilege registry for GET & HEAD
2463 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002464 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302465 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2466 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002467 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2468 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2469 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2470 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2471 }
2472
2473 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002474 void doPost(crow::Response& res, const crow::Request& req,
2475 const std::vector<std::string>& params) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002476 {
2477 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2478
2479 crow::connections::systemBus->async_method_call(
2480 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002481 const std::string& resp) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002482 if (ec)
2483 {
2484 messages::internalError(asyncResp->res);
2485 return;
2486 }
2487 messages::success(asyncResp->res);
2488 },
2489 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2490 }
2491};
2492
Jason M. Billse855dd22019-10-08 11:37:48 -07002493static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002494 const std::string& logID,
2495 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002496{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002497 auto getStoredLogCallback =
2498 [asyncResp, logID, &logEntryJson](
2499 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002500 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002501 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002502 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002503 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2504 if (ec.value() ==
2505 boost::system::linux_error::bad_request_descriptor)
2506 {
2507 messages::resourceNotFound(asyncResp->res, "LogEntry",
2508 logID);
2509 }
2510 else
2511 {
2512 messages::internalError(asyncResp->res);
2513 }
2514 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002515 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002516
Johnathan Mantey043a0532020-03-10 17:15:28 -07002517 std::string timestamp{};
2518 std::string filename{};
2519 std::string logfile{};
2520 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2521
2522 if (filename.empty() || timestamp.empty())
2523 {
2524 messages::resourceMissingAtURI(asyncResp->res, logID);
2525 return;
2526 }
2527
2528 std::string crashdumpURI =
2529 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2530 logID + "/" + filename;
2531 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2532 {"@odata.id", "/redfish/v1/Systems/system/"
2533 "LogServices/Crashdump/Entries/" +
2534 logID},
2535 {"Name", "CPU Crashdump"},
2536 {"Id", logID},
2537 {"EntryType", "Oem"},
2538 {"OemRecordFormat", "Crashdump URI"},
2539 {"Message", std::move(crashdumpURI)},
2540 {"Created", std::move(timestamp)}};
2541 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002542 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002543 std::move(getStoredLogCallback), crashdumpObject,
2544 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002545 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002546}
2547
Jason M. Bills424c4172019-03-21 13:50:33 -07002548class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002549{
2550 public:
2551 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002552 CrashdumpEntryCollection(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002553 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002554 {
AppaRao Puli39460282020-04-07 17:03:04 +05302555 // Note: Deviated from redfish privilege registry for GET & HEAD
2556 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002557 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302558 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2559 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002560 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2561 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2562 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2563 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002564 }
2565
2566 private:
2567 /**
2568 * Functions triggers appropriate requests on DBus
2569 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002570 void doGet(crow::Response& res, const crow::Request& req,
2571 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002572 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002573 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002574 // Collections don't include the static data added by SubRoute because
2575 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002576 auto getLogEntriesCallback = [asyncResp](
2577 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002578 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002579 if (ec)
2580 {
2581 if (ec.value() !=
2582 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002583 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002584 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2585 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002586 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002587 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002588 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002589 }
2590 asyncResp->res.jsonValue["@odata.type"] =
2591 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002592 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002593 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002594 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002595 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002596 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002597 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002598 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002599 std::vector<std::string> logIDs;
2600 // Get the list of log entries and build up an empty array big
2601 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002602 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002603 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002604 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002605 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002606 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002607 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002608 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002609 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002610 logIDs.emplace_back(objpath.substr(lastPos + 1));
2611
2612 // Add a space for the log entry to the array
2613 logEntryArray.push_back({});
2614 }
2615 // Now go through and set up async calls to fill in the entries
2616 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002617 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002618 {
2619 // Add the log entry to the array
2620 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002621 }
2622 asyncResp->res.jsonValue["Members@odata.count"] =
2623 logEntryArray.size();
2624 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002625 crow::connections::systemBus->async_method_call(
2626 std::move(getLogEntriesCallback),
2627 "xyz.openbmc_project.ObjectMapper",
2628 "/xyz/openbmc_project/object_mapper",
2629 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002630 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002631 }
2632};
2633
Jason M. Bills424c4172019-03-21 13:50:33 -07002634class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002635{
2636 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002637 CrashdumpEntry(CrowApp& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002638 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002639 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002640 std::string())
2641 {
AppaRao Puli39460282020-04-07 17:03:04 +05302642 // Note: Deviated from redfish privilege registry for GET & HEAD
2643 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002644 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302645 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2646 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002647 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2648 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2649 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2650 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002651 }
2652
2653 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002654 void doGet(crow::Response& res, const crow::Request& req,
2655 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002656 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002657 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002658 if (params.size() != 1)
2659 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002660 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002661 return;
2662 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002663 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002664 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2665 }
2666};
2667
2668class CrashdumpFile : public Node
2669{
2670 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002671 CrashdumpFile(CrowApp& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002672 Node(app,
2673 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2674 "<str>/",
2675 std::string(), std::string())
2676 {
AppaRao Puli39460282020-04-07 17:03:04 +05302677 // Note: Deviated from redfish privilege registry for GET & HEAD
2678 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002679 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302680 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2681 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002682 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2683 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2684 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2685 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2686 }
2687
2688 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002689 void doGet(crow::Response& res, const crow::Request& req,
2690 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002691 {
2692 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2693 if (params.size() != 2)
2694 {
2695 messages::internalError(asyncResp->res);
2696 return;
2697 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002698 const std::string& logID = params[0];
2699 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002700
Johnathan Mantey043a0532020-03-10 17:15:28 -07002701 auto getStoredLogCallback =
2702 [asyncResp, logID, fileName](
2703 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002704 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002705 if (ec)
2706 {
2707 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2708 << ec.message();
2709 messages::internalError(asyncResp->res);
2710 return;
2711 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002712
Johnathan Mantey043a0532020-03-10 17:15:28 -07002713 std::string dbusFilename{};
2714 std::string dbusTimestamp{};
2715 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002716
Johnathan Mantey043a0532020-03-10 17:15:28 -07002717 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2718 dbusFilepath);
2719
2720 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2721 dbusFilepath.empty())
2722 {
2723 messages::resourceMissingAtURI(asyncResp->res, fileName);
2724 return;
2725 }
2726
2727 // Verify the file name parameter is correct
2728 if (fileName != dbusFilename)
2729 {
2730 messages::resourceMissingAtURI(asyncResp->res, fileName);
2731 return;
2732 }
2733
2734 if (!std::filesystem::exists(dbusFilepath))
2735 {
2736 messages::resourceMissingAtURI(asyncResp->res, fileName);
2737 return;
2738 }
2739 std::ifstream ifs(dbusFilepath, std::ios::in |
2740 std::ios::binary |
2741 std::ios::ate);
2742 std::ifstream::pos_type fileSize = ifs.tellg();
2743 if (fileSize < 0)
2744 {
2745 messages::generalError(asyncResp->res);
2746 return;
2747 }
2748 ifs.seekg(0, std::ios::beg);
2749
2750 auto crashData = std::make_unique<char[]>(
2751 static_cast<unsigned int>(fileSize));
2752
2753 ifs.read(crashData.get(), static_cast<int>(fileSize));
2754
2755 // The cast to std::string is intentional in order to use the
2756 // assign() that applies move mechanics
2757 asyncResp->res.body().assign(
2758 static_cast<std::string>(crashData.get()));
2759
2760 // Configure this to be a file download when accessed from
2761 // a browser
2762 asyncResp->res.addHeader("Content-Disposition", "attachment");
2763 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002764 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002765 std::move(getStoredLogCallback), crashdumpObject,
2766 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002767 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002768 }
2769};
2770
Jason M. Bills424c4172019-03-21 13:50:33 -07002771class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002772{
2773 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002774 OnDemandCrashdump(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002775 Node(app,
2776 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2777 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002778 {
AppaRao Puli39460282020-04-07 17:03:04 +05302779 // Note: Deviated from redfish privilege registry for GET & HEAD
2780 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002781 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302782 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2783 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2784 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2785 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2786 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2787 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002788 }
2789
2790 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002791 void doPost(crow::Response& res, const crow::Request& req,
2792 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002793 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002794 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002795
James Feistfe306722020-03-12 16:32:08 -07002796 auto generateonDemandLogCallback = [asyncResp,
2797 req](const boost::system::error_code
2798 ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002799 const std::string& resp) {
James Feist46229572020-02-19 15:11:58 -08002800 if (ec)
2801 {
2802 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002803 {
James Feist46229572020-02-19 15:11:58 -08002804 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002805 }
James Feist46229572020-02-19 15:11:58 -08002806 else if (ec.value() ==
2807 boost::system::errc::device_or_resource_busy)
2808 {
2809 messages::serviceTemporarilyUnavailable(asyncResp->res,
2810 "60");
2811 }
2812 else
2813 {
2814 messages::internalError(asyncResp->res);
2815 }
2816 return;
2817 }
2818 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002819 [](boost::system::error_code err, sdbusplus::message::message&,
2820 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002821 if (!err)
2822 {
James Feiste5d50062020-05-11 17:29:00 -07002823 taskData->messages.emplace_back(
2824 messages::taskCompletedOK(
2825 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002826 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002827 }
James Feist32898ce2020-03-10 16:16:52 -07002828 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002829 },
James Feist46229572020-02-19 15:11:58 -08002830 "type='signal',interface='org.freedesktop.DBus.Properties',"
2831 "member='PropertiesChanged',arg0namespace='com.intel."
2832 "crashdump'");
2833 task->startTimer(std::chrono::minutes(5));
2834 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002835 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002836 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002837 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002838 std::move(generateonDemandLogCallback), crashdumpObject,
2839 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002840 }
2841};
2842
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002843class TelemetryCrashdump : public Node
2844{
2845 public:
2846 TelemetryCrashdump(CrowApp& app) :
2847 Node(app,
2848 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2849 "Crashdump.Telemetry/")
2850 {
2851 // Note: Deviated from redfish privilege registry for GET & HEAD
2852 // method for security reasons.
2853 entityPrivileges = {
2854 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2855 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2856 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2857 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2858 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2859 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2860 }
2861
2862 private:
2863 void doPost(crow::Response& res, const crow::Request& req,
2864 const std::vector<std::string>& params) override
2865 {
2866 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2867
2868 auto generateTelemetryLogCallback = [asyncResp, req](
2869 const boost::system::error_code
2870 ec,
2871 const std::string& resp) {
2872 if (ec)
2873 {
2874 if (ec.value() == boost::system::errc::operation_not_supported)
2875 {
2876 messages::resourceInStandby(asyncResp->res);
2877 }
2878 else if (ec.value() ==
2879 boost::system::errc::device_or_resource_busy)
2880 {
2881 messages::serviceTemporarilyUnavailable(asyncResp->res,
2882 "60");
2883 }
2884 else
2885 {
2886 messages::internalError(asyncResp->res);
2887 }
2888 return;
2889 }
2890 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2891 [](boost::system::error_code err, sdbusplus::message::message&,
2892 const std::shared_ptr<task::TaskData>& taskData) {
2893 if (!err)
2894 {
2895 taskData->messages.emplace_back(
2896 messages::taskCompletedOK(
2897 std::to_string(taskData->index)));
2898 taskData->state = "Completed";
2899 }
2900 return task::completed;
2901 },
2902 "type='signal',interface='org.freedesktop.DBus.Properties',"
2903 "member='PropertiesChanged',arg0namespace='com.intel."
2904 "crashdump'");
2905 task->startTimer(std::chrono::minutes(5));
2906 task->populateResp(asyncResp->res);
2907 task->payload.emplace(req);
2908 };
2909 crow::connections::systemBus->async_method_call(
2910 std::move(generateTelemetryLogCallback), crashdumpObject,
2911 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2912 }
2913};
2914
Jason M. Billse1f26342018-07-18 12:12:00 -07002915class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002916{
2917 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002918 SendRawPECI(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002919 Node(app,
2920 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2921 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002922 {
AppaRao Puli39460282020-04-07 17:03:04 +05302923 // Note: Deviated from redfish privilege registry for GET & HEAD
2924 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002925 entityPrivileges = {
2926 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2927 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2928 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2929 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2930 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2931 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2932 }
2933
2934 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002935 void doPost(crow::Response& res, const crow::Request& req,
2936 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002937 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002938 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002939 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002940
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002941 nlohmann::json reqJson =
2942 nlohmann::json::parse(req.body, nullptr, false);
2943 if (reqJson.find("PECICommands") != reqJson.end())
2944 {
2945 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2946 {
2947 return;
2948 }
2949 uint32_t idx = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002950 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002951 {
2952 if (cmd.size() < 3)
2953 {
2954 std::string s("[");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002955 for (auto const& val : cmd)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002956 {
2957 if (val != *cmd.begin())
2958 {
2959 s += ",";
2960 }
2961 s += std::to_string(val);
2962 }
2963 s += "]";
2964 messages::actionParameterValueFormatError(
2965 res, s, "PECICommands[" + std::to_string(idx) + "]",
2966 "SendRawPeci");
2967 return;
2968 }
2969 idx++;
2970 }
2971 }
2972 else
2973 {
2974 /* This interface is deprecated */
2975 uint8_t clientAddress = 0;
2976 uint8_t readLength = 0;
2977 std::vector<uint8_t> peciCommand;
2978 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2979 "ReadLength", readLength, "PECICommand",
2980 peciCommand))
2981 {
2982 return;
2983 }
2984 peciCommands.push_back({clientAddress, 0, readLength});
2985 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2986 peciCommand.end());
2987 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002988 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002989 auto sendRawPECICallback =
2990 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002991 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002992 if (ec)
2993 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002994 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002995 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002996 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002997 return;
2998 }
2999 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
3000 {"PECIResponse", resp}};
3001 };
Ed Tanous1da66f72018-07-27 16:13:37 -07003002 // Call the SendRawPECI command with the provided data
3003 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07003004 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08003005 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07003006 }
3007};
3008
Andrew Geisslercb92c032018-08-17 07:56:14 -07003009/**
3010 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3011 */
3012class DBusLogServiceActionsClear : public Node
3013{
3014 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003015 DBusLogServiceActionsClear(CrowApp& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07003016 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05003017 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07003018 {
3019 entityPrivileges = {
3020 {boost::beast::http::verb::get, {{"Login"}}},
3021 {boost::beast::http::verb::head, {{"Login"}}},
3022 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3023 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3024 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3025 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3026 }
3027
3028 private:
3029 /**
3030 * Function handles POST method request.
3031 * The Clear Log actions does not require any parameter.The action deletes
3032 * all entries found in the Entries collection for this Log Service.
3033 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003034 void doPost(crow::Response& res, const crow::Request& req,
3035 const std::vector<std::string>& params) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07003036 {
3037 BMCWEB_LOG_DEBUG << "Do delete all entries.";
3038
3039 auto asyncResp = std::make_shared<AsyncResp>(res);
3040 // Process response from Logging service.
3041 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
3042 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3043 if (ec)
3044 {
3045 // TODO Handle for specific error code
3046 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3047 asyncResp->res.result(
3048 boost::beast::http::status::internal_server_error);
3049 return;
3050 }
3051
3052 asyncResp->res.result(boost::beast::http::status::no_content);
3053 };
3054
3055 // Make call to Logging service to request Clear Log
3056 crow::connections::systemBus->async_method_call(
3057 resp_handler, "xyz.openbmc_project.Logging",
3058 "/xyz/openbmc_project/logging",
3059 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3060 }
3061};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003062
3063/****************************************************
3064 * Redfish PostCode interfaces
3065 * using DBUS interface: getPostCodesTS
3066 ******************************************************/
3067class PostCodesLogService : public Node
3068{
3069 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003070 PostCodesLogService(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003071 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
3072 {
3073 entityPrivileges = {
3074 {boost::beast::http::verb::get, {{"Login"}}},
3075 {boost::beast::http::verb::head, {{"Login"}}},
3076 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3077 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3078 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3079 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3080 }
3081
3082 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003083 void doGet(crow::Response& res, const crow::Request& req,
3084 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003085 {
3086 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3087
3088 asyncResp->res.jsonValue = {
3089 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
3090 {"@odata.type", "#LogService.v1_1_0.LogService"},
3091 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
3092 {"Name", "POST Code Log Service"},
3093 {"Description", "POST Code Log Service"},
3094 {"Id", "BIOS POST Code Log"},
3095 {"OverWritePolicy", "WrapsWhenFull"},
3096 {"Entries",
3097 {{"@odata.id",
3098 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
3099 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3100 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
3101 "Actions/LogService.ClearLog"}};
3102 }
3103};
3104
3105class PostCodesClear : public Node
3106{
3107 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003108 PostCodesClear(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003109 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
3110 "LogService.ClearLog/")
3111 {
3112 entityPrivileges = {
3113 {boost::beast::http::verb::get, {{"Login"}}},
3114 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05303115 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
3116 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
3117 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
3118 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003119 }
3120
3121 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003122 void doPost(crow::Response& res, const crow::Request& req,
3123 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003124 {
3125 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3126
3127 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3128 // Make call to post-code service to request clear all
3129 crow::connections::systemBus->async_method_call(
3130 [asyncResp](const boost::system::error_code ec) {
3131 if (ec)
3132 {
3133 // TODO Handle for specific error code
3134 BMCWEB_LOG_ERROR
3135 << "doClearPostCodes resp_handler got error " << ec;
3136 asyncResp->res.result(
3137 boost::beast::http::status::internal_server_error);
3138 messages::internalError(asyncResp->res);
3139 return;
3140 }
3141 },
3142 "xyz.openbmc_project.State.Boot.PostCode",
3143 "/xyz/openbmc_project/State/Boot/PostCode",
3144 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3145 }
3146};
3147
3148static void fillPostCodeEntry(
3149 std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003150 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003151 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3152 const uint64_t skip = 0, const uint64_t top = 0)
3153{
3154 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003155 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08003156 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003157
3158 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003159 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003160
3161 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003162 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003163 {
3164 currentCodeIndex++;
3165 std::string postcodeEntryID =
3166 "B" + std::to_string(bootIndex) + "-" +
3167 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3168
3169 uint64_t usecSinceEpoch = code.first;
3170 uint64_t usTimeOffset = 0;
3171
3172 if (1 == currentCodeIndex)
3173 { // already incremented
3174 firstCodeTimeUs = code.first;
3175 }
3176 else
3177 {
3178 usTimeOffset = code.first - firstCodeTimeUs;
3179 }
3180
3181 // skip if no specific codeIndex is specified and currentCodeIndex does
3182 // not fall between top and skip
3183 if ((codeIndex == 0) &&
3184 (currentCodeIndex <= skip || currentCodeIndex > top))
3185 {
3186 continue;
3187 }
3188
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003189 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003190 // currentIndex
3191 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3192 {
3193 // This is done for simplicity. 1st entry is needed to calculate
3194 // time offset. To improve efficiency, one can get to the entry
3195 // directly (possibly with flatmap's nth method)
3196 continue;
3197 }
3198
3199 // currentCodeIndex is within top and skip or equal to specified code
3200 // index
3201
3202 // Get the Created time from the timestamp
3203 std::string entryTimeStr;
3204 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
3205 {
3206 continue;
3207 }
3208
3209 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3210 std::ostringstream hexCode;
3211 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3212 << code.second;
3213 std::ostringstream timeOffsetStr;
3214 // Set Fixed -Point Notation
3215 timeOffsetStr << std::fixed;
3216 // Set precision to 4 digits
3217 timeOffsetStr << std::setprecision(4);
3218 // Add double to stream
3219 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3220 std::vector<std::string> messageArgs = {
3221 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3222
3223 // Get MessageArgs template from message registry
3224 std::string msg;
3225 if (message != nullptr)
3226 {
3227 msg = message->message;
3228
3229 // fill in this post code value
3230 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003231 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003232 {
3233 std::string argStr = "%" + std::to_string(++i);
3234 size_t argPos = msg.find(argStr);
3235 if (argPos != std::string::npos)
3236 {
3237 msg.replace(argPos, argStr.length(), messageArg);
3238 }
3239 }
3240 }
3241
Tim Leed4342a92020-04-27 11:47:58 +08003242 // Get Severity template from message registry
3243 std::string severity;
3244 if (message != nullptr)
3245 {
3246 severity = message->severity;
3247 }
3248
ZhikuiRena3316fc2020-01-29 14:58:08 -08003249 // add to AsyncResp
3250 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003251 nlohmann::json& bmcLogEntry = logEntryArray.back();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003252 bmcLogEntry = {
3253 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
3254 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
3255 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
3256 "PostCodes/Entries/" +
3257 postcodeEntryID},
3258 {"Name", "POST Code Log Entry"},
3259 {"Id", postcodeEntryID},
3260 {"Message", std::move(msg)},
3261 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
3262 {"MessageArgs", std::move(messageArgs)},
3263 {"EntryType", "Event"},
3264 {"Severity", std::move(severity)},
3265 {"Created", std::move(entryTimeStr)}};
3266 }
3267}
3268
3269static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
3270 const uint16_t bootIndex,
3271 const uint64_t codeIndex)
3272{
3273 crow::connections::systemBus->async_method_call(
3274 [aResp, bootIndex, codeIndex](
3275 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003276 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003277 if (ec)
3278 {
3279 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3280 messages::internalError(aResp->res);
3281 return;
3282 }
3283
3284 // skip the empty postcode boots
3285 if (postcode.empty())
3286 {
3287 return;
3288 }
3289
3290 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3291
3292 aResp->res.jsonValue["Members@odata.count"] =
3293 aResp->res.jsonValue["Members"].size();
3294 },
3295 "xyz.openbmc_project.State.Boot.PostCode",
3296 "/xyz/openbmc_project/State/Boot/PostCode",
3297 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3298 bootIndex);
3299}
3300
3301static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
3302 const uint16_t bootIndex,
3303 const uint16_t bootCount,
3304 const uint64_t entryCount, const uint64_t skip,
3305 const uint64_t top)
3306{
3307 crow::connections::systemBus->async_method_call(
3308 [aResp, bootIndex, bootCount, entryCount, skip,
3309 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003310 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003311 if (ec)
3312 {
3313 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3314 messages::internalError(aResp->res);
3315 return;
3316 }
3317
3318 uint64_t endCount = entryCount;
3319 if (!postcode.empty())
3320 {
3321 endCount = entryCount + postcode.size();
3322
3323 if ((skip < endCount) && ((top + skip) > entryCount))
3324 {
3325 uint64_t thisBootSkip =
3326 std::max(skip, entryCount) - entryCount;
3327 uint64_t thisBootTop =
3328 std::min(top + skip, endCount) - entryCount;
3329
3330 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3331 thisBootSkip, thisBootTop);
3332 }
3333 aResp->res.jsonValue["Members@odata.count"] = endCount;
3334 }
3335
3336 // continue to previous bootIndex
3337 if (bootIndex < bootCount)
3338 {
3339 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3340 bootCount, endCount, skip, top);
3341 }
3342 else
3343 {
3344 aResp->res.jsonValue["Members@odata.nextLink"] =
3345 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3346 "Entries?$skip=" +
3347 std::to_string(skip + top);
3348 }
3349 },
3350 "xyz.openbmc_project.State.Boot.PostCode",
3351 "/xyz/openbmc_project/State/Boot/PostCode",
3352 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3353 bootIndex);
3354}
3355
3356static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
3357 const uint64_t skip, const uint64_t top)
3358{
3359 uint64_t entryCount = 0;
3360 crow::connections::systemBus->async_method_call(
3361 [aResp, entryCount, skip,
3362 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003363 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003364 if (ec)
3365 {
3366 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3367 messages::internalError(aResp->res);
3368 return;
3369 }
3370 auto pVal = std::get_if<uint16_t>(&bootCount);
3371 if (pVal)
3372 {
3373 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3374 }
3375 else
3376 {
3377 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3378 }
3379 },
3380 "xyz.openbmc_project.State.Boot.PostCode",
3381 "/xyz/openbmc_project/State/Boot/PostCode",
3382 "org.freedesktop.DBus.Properties", "Get",
3383 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3384}
3385
3386class PostCodesEntryCollection : public Node
3387{
3388 public:
3389 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003390 PostCodesEntryCollection(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003391 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3392 {
3393 entityPrivileges = {
3394 {boost::beast::http::verb::get, {{"Login"}}},
3395 {boost::beast::http::verb::head, {{"Login"}}},
3396 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3397 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3398 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3399 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3400 }
3401
3402 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003403 void doGet(crow::Response& res, const crow::Request& req,
3404 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003405 {
3406 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3407
3408 asyncResp->res.jsonValue["@odata.type"] =
3409 "#LogEntryCollection.LogEntryCollection";
3410 asyncResp->res.jsonValue["@odata.context"] =
3411 "/redfish/v1/"
3412 "$metadata#LogEntryCollection.LogEntryCollection";
3413 asyncResp->res.jsonValue["@odata.id"] =
3414 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3415 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3416 asyncResp->res.jsonValue["Description"] =
3417 "Collection of POST Code Log Entries";
3418 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3419 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3420
3421 uint64_t skip = 0;
3422 uint64_t top = maxEntriesPerPage; // Show max entries by default
3423 if (!getSkipParam(asyncResp->res, req, skip))
3424 {
3425 return;
3426 }
3427 if (!getTopParam(asyncResp->res, req, top))
3428 {
3429 return;
3430 }
3431 getCurrentBootNumber(asyncResp, skip, top);
3432 }
3433};
3434
3435class PostCodesEntry : public Node
3436{
3437 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003438 PostCodesEntry(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003439 Node(app,
3440 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
3441 std::string())
3442 {
3443 entityPrivileges = {
3444 {boost::beast::http::verb::get, {{"Login"}}},
3445 {boost::beast::http::verb::head, {{"Login"}}},
3446 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3447 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3448 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3449 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3450 }
3451
3452 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003453 void doGet(crow::Response& res, const crow::Request& req,
3454 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003455 {
3456 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3457 if (params.size() != 1)
3458 {
3459 messages::internalError(asyncResp->res);
3460 return;
3461 }
3462
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003463 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003464
3465 size_t bootPos = targetID.find('B');
3466 if (bootPos == std::string::npos)
3467 {
3468 // Requested ID was not found
3469 messages::resourceMissingAtURI(asyncResp->res, targetID);
3470 return;
3471 }
3472 std::string_view bootIndexStr(targetID);
3473 bootIndexStr.remove_prefix(bootPos + 1);
3474 uint16_t bootIndex = 0;
3475 uint64_t codeIndex = 0;
3476 size_t dashPos = bootIndexStr.find('-');
3477
3478 if (dashPos == std::string::npos)
3479 {
3480 return;
3481 }
3482 std::string_view codeIndexStr(bootIndexStr);
3483 bootIndexStr.remove_suffix(dashPos);
3484 codeIndexStr.remove_prefix(dashPos + 1);
3485
3486 bootIndex = static_cast<uint16_t>(
3487 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
3488 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
3489 if (bootIndex == 0 || codeIndex == 0)
3490 {
3491 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3492 << params[0];
3493 }
3494
3495 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3496 asyncResp->res.jsonValue["@odata.context"] =
3497 "/redfish/v1/$metadata#LogEntry.LogEntry";
3498 asyncResp->res.jsonValue["@odata.id"] =
3499 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3500 "Entries";
3501 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3502 asyncResp->res.jsonValue["Description"] =
3503 "Collection of POST Code Log Entries";
3504 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3505 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3506
3507 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3508 }
3509};
3510
Ed Tanous1da66f72018-07-27 16:13:37 -07003511} // namespace redfish