blob: c4d4e891d754f151be895e6264ba5faf9d4e4dbb [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
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500182static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800183{
184 int ret = 0;
185 uint64_t timestamp = 0;
186 ret = sd_journal_get_realtime_usec(journal, &timestamp);
187 if (ret < 0)
188 {
189 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
190 << strerror(-ret);
191 return false;
192 }
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500193 entryTimestamp = crow::utility::getDateTime(
194 static_cast<std::time_t>(timestamp / 1000 / 1000));
195 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800196}
197
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500198static bool getSkipParam(crow::Response& res, const crow::Request& req,
199 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700200{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500201 char* skipParam = req.urlParams.get("$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700202 if (skipParam != nullptr)
203 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700205 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700206 if (*skipParam == '\0' || *ptr != '\0')
207 {
208
209 messages::queryParameterValueTypeError(res, std::string(skipParam),
210 "$skip");
211 return false;
212 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700213 }
214 return true;
215}
216
Ed Tanous271584a2019-07-09 16:24:22 -0700217static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500218static bool getTopParam(crow::Response& res, const crow::Request& req,
219 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700220{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500221 char* topParam = req.urlParams.get("$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700222 if (topParam != nullptr)
223 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500224 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700225 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700226 if (*topParam == '\0' || *ptr != '\0')
227 {
228 messages::queryParameterValueTypeError(res, std::string(topParam),
229 "$top");
230 return false;
231 }
Ed Tanous271584a2019-07-09 16:24:22 -0700232 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700233 {
234
235 messages::queryParameterOutOfRange(
236 res, std::to_string(top), "$top",
237 "1-" + std::to_string(maxEntriesPerPage));
238 return false;
239 }
240 }
241 return true;
242}
243
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500244static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700245 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700246{
247 int ret = 0;
248 static uint64_t prevTs = 0;
249 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700250 if (firstEntry)
251 {
252 prevTs = 0;
253 }
254
Jason M. Bills16428a12018-11-02 12:42:29 -0700255 // Get the entry timestamp
256 uint64_t curTs = 0;
257 ret = sd_journal_get_realtime_usec(journal, &curTs);
258 if (ret < 0)
259 {
260 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
261 << strerror(-ret);
262 return false;
263 }
264 // If the timestamp isn't unique, increment the index
265 if (curTs == prevTs)
266 {
267 index++;
268 }
269 else
270 {
271 // Otherwise, reset it
272 index = 0;
273 }
274 // Save the timestamp
275 prevTs = curTs;
276
277 entryID = std::to_string(curTs);
278 if (index > 0)
279 {
280 entryID += "_" + std::to_string(index);
281 }
282 return true;
283}
284
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500285static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700286 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700287{
Ed Tanous271584a2019-07-09 16:24:22 -0700288 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700289 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700290 if (firstEntry)
291 {
292 prevTs = 0;
293 }
294
Jason M. Bills95820182019-04-22 16:25:34 -0700295 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700296 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700297 std::tm timeStruct = {};
298 std::istringstream entryStream(logEntry);
299 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
300 {
301 curTs = std::mktime(&timeStruct);
302 }
303 // If the timestamp isn't unique, increment the index
304 if (curTs == prevTs)
305 {
306 index++;
307 }
308 else
309 {
310 // Otherwise, reset it
311 index = 0;
312 }
313 // Save the timestamp
314 prevTs = curTs;
315
316 entryID = std::to_string(curTs);
317 if (index > 0)
318 {
319 entryID += "_" + std::to_string(index);
320 }
321 return true;
322}
323
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500324static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
325 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700326{
327 if (entryID.empty())
328 {
329 return false;
330 }
331 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800332 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700333
334 auto underscorePos = tsStr.find("_");
335 if (underscorePos != tsStr.npos)
336 {
337 // Timestamp has an index
338 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800339 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 indexStr.remove_prefix(underscorePos + 1);
341 std::size_t pos;
342 try
343 {
Ed Tanous39e77502019-03-04 17:35:53 -0800344 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700345 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500346 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700347 {
348 messages::resourceMissingAtURI(res, entryID);
349 return false;
350 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500351 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700352 {
353 messages::resourceMissingAtURI(res, entryID);
354 return false;
355 }
356 if (pos != indexStr.size())
357 {
358 messages::resourceMissingAtURI(res, entryID);
359 return false;
360 }
361 }
362 // Timestamp has no index
363 std::size_t pos;
364 try
365 {
Ed Tanous39e77502019-03-04 17:35:53 -0800366 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700367 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500368 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700369 {
370 messages::resourceMissingAtURI(res, entryID);
371 return false;
372 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500373 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700374 {
375 messages::resourceMissingAtURI(res, entryID);
376 return false;
377 }
378 if (pos != tsStr.size())
379 {
380 messages::resourceMissingAtURI(res, entryID);
381 return false;
382 }
383 return true;
384}
385
Jason M. Bills95820182019-04-22 16:25:34 -0700386static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500387 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700388{
389 static const std::filesystem::path redfishLogDir = "/var/log";
390 static const std::string redfishLogFilename = "redfish";
391
392 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500393 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700394 std::filesystem::directory_iterator(redfishLogDir))
395 {
396 // If we find a redfish log file, save the path
397 std::string filename = dirEnt.path().filename();
398 if (boost::starts_with(filename, redfishLogFilename))
399 {
400 redfishLogFiles.emplace_back(redfishLogDir / filename);
401 }
402 }
403 // As the log files rotate, they are appended with a ".#" that is higher for
404 // the older logs. Since we don't expect more than 10 log files, we
405 // can just sort the list to get them in order from newest to oldest
406 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
407
408 return !redfishLogFiles.empty();
409}
410
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500411inline void getDumpEntryCollection(std::shared_ptr<AsyncResp>& asyncResp,
412 const std::string& dumpType)
413{
414 std::string dumpPath;
415 if (dumpType == "BMC")
416 {
417 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
418 }
419 else if (dumpType == "System")
420 {
421 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
422 }
423 else
424 {
425 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
426 messages::internalError(asyncResp->res);
427 return;
428 }
429
430 crow::connections::systemBus->async_method_call(
431 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
432 GetManagedObjectsType& resp) {
433 if (ec)
434 {
435 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
436 messages::internalError(asyncResp->res);
437 return;
438 }
439
440 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
441 entriesArray = nlohmann::json::array();
442
443 for (auto& object : resp)
444 {
445 bool foundDumpEntry = false;
446 for (auto& interfaceMap : object.second)
447 {
448 if (interfaceMap.first ==
449 ("xyz.openbmc_project.Dump.Entry." + dumpType))
450 {
451 foundDumpEntry = true;
452 break;
453 }
454 }
455
456 if (foundDumpEntry == false)
457 {
458 continue;
459 }
460 std::time_t timestamp;
461 uint64_t size = 0;
462 entriesArray.push_back({});
463 nlohmann::json& thisEntry = entriesArray.back();
464 const std::string& path =
465 static_cast<const std::string&>(object.first);
466 std::size_t lastPos = path.rfind("/");
467 if (lastPos == std::string::npos)
468 {
469 continue;
470 }
471 std::string entryID = path.substr(lastPos + 1);
472
473 for (auto& interfaceMap : object.second)
474 {
475 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
476 {
477
478 for (auto& propertyMap : interfaceMap.second)
479 {
480 if (propertyMap.first == "Size")
481 {
482 auto sizePtr =
483 std::get_if<uint64_t>(&propertyMap.second);
484 if (sizePtr == nullptr)
485 {
486 messages::internalError(asyncResp->res);
487 break;
488 }
489 size = *sizePtr;
490 break;
491 }
492 }
493 }
494 else if (interfaceMap.first ==
495 "xyz.openbmc_project.Time.EpochTime")
496 {
497
498 for (auto& propertyMap : interfaceMap.second)
499 {
500 if (propertyMap.first == "Elapsed")
501 {
502 const uint64_t* usecsTimeStamp =
503 std::get_if<uint64_t>(&propertyMap.second);
504 if (usecsTimeStamp == nullptr)
505 {
506 messages::internalError(asyncResp->res);
507 break;
508 }
509 timestamp =
510 static_cast<std::time_t>(*usecsTimeStamp);
511 break;
512 }
513 }
514 }
515 }
516
517 thisEntry["@odata.type"] = "#LogEntry.v1_5_1.LogEntry";
518 thisEntry["@odata.id"] = dumpPath + entryID;
519 thisEntry["Id"] = entryID;
520 thisEntry["EntryType"] = "Event";
521 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
522 thisEntry["Name"] = dumpType + " Dump Entry";
523
524 thisEntry["Oem"]["OpenBmc"]["@odata.type"] =
525 "#OemLogEntry.v1_0_0.OpenBmc";
526 thisEntry["Oem"]["OpenBmc"]["AdditionalDataSizeBytes"] = size;
527
528 if (dumpType == "BMC")
529 {
530 thisEntry["Oem"]["OpenBmc"]["DiagnosticDataType"] =
531 "Manager";
532 thisEntry["Oem"]["OpenBmc"]["AdditionalDataURI"] =
533 "/redfish/v1/Managers/bmc/LogServices/Dump/"
534 "attachment/" +
535 entryID;
536 }
537 else if (dumpType == "System")
538 {
539 thisEntry["Oem"]["OpenBmc"]["DiagnosticDataType"] = "OEM";
540 thisEntry["Oem"]["OpenBmc"]["OEMDiagnosticDataType"] =
541 "System";
542 thisEntry["Oem"]["OpenBmc"]["AdditionalDataURI"] =
543 "/redfish/v1/Systems/system/LogServices/Dump/"
544 "attachment/" +
545 entryID;
546 }
547 }
548 asyncResp->res.jsonValue["Members@odata.count"] =
549 entriesArray.size();
550 },
551 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
552 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
553}
554
555inline void getDumpEntryById(std::shared_ptr<AsyncResp>& asyncResp,
556 const std::string& entryID,
557 const std::string& dumpType)
558{
559 std::string dumpPath;
560 if (dumpType == "BMC")
561 {
562 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
563 }
564 else if (dumpType == "System")
565 {
566 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
567 }
568 else
569 {
570 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
571 messages::internalError(asyncResp->res);
572 return;
573 }
574
575 crow::connections::systemBus->async_method_call(
576 [asyncResp, entryID, dumpPath, dumpType](
577 const boost::system::error_code ec, GetManagedObjectsType& resp) {
578 if (ec)
579 {
580 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
581 messages::internalError(asyncResp->res);
582 return;
583 }
584
585 for (auto& objectPath : resp)
586 {
587 if (objectPath.first.str.find(
588 "/xyz/openbmc_project/dump/entry/" + entryID) ==
589 std::string::npos)
590 {
591 continue;
592 }
593
594 bool foundDumpEntry = false;
595 for (auto& interfaceMap : objectPath.second)
596 {
597 if (interfaceMap.first ==
598 ("xyz.openbmc_project.Dump.Entry." + dumpType))
599 {
600 foundDumpEntry = true;
601 break;
602 }
603 }
604 if (foundDumpEntry == false)
605 {
606 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
607 messages::internalError(asyncResp->res);
608 return;
609 }
610
611 std::time_t timestamp;
612 uint64_t size = 0;
613
614 for (auto& interfaceMap : objectPath.second)
615 {
616 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
617 {
618 for (auto& propertyMap : interfaceMap.second)
619 {
620 if (propertyMap.first == "Size")
621 {
622 auto sizePtr =
623 std::get_if<uint64_t>(&propertyMap.second);
624 if (sizePtr == nullptr)
625 {
626 messages::internalError(asyncResp->res);
627 break;
628 }
629 size = *sizePtr;
630 break;
631 }
632 }
633 }
634 else if (interfaceMap.first ==
635 "xyz.openbmc_project.Time.EpochTime")
636 {
637 for (auto& propertyMap : interfaceMap.second)
638 {
639 if (propertyMap.first == "Elapsed")
640 {
641 const uint64_t* usecsTimeStamp =
642 std::get_if<uint64_t>(&propertyMap.second);
643 if (usecsTimeStamp == nullptr)
644 {
645 messages::internalError(asyncResp->res);
646 break;
647 }
648 timestamp =
649 static_cast<std::time_t>(*usecsTimeStamp);
650 break;
651 }
652 }
653 }
654 }
655
656 asyncResp->res.jsonValue["@odata.type"] =
657 "#LogEntry.v1_5_1.LogEntry";
658 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
659 asyncResp->res.jsonValue["Id"] = entryID;
660 asyncResp->res.jsonValue["EntryType"] = "Event";
661 asyncResp->res.jsonValue["Created"] =
662 crow::utility::getDateTime(timestamp);
663 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
664
665 asyncResp->res.jsonValue["Oem"]["OpenBmc"]["@odata.type"] =
666 "#OemLogEntry.v1_0_0.OpenBmc";
667 asyncResp->res
668 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataSizeBytes"] =
669 size;
670
671 if (dumpType == "BMC")
672 {
673 asyncResp->res
674 .jsonValue["Oem"]["OpenBmc"]["DiagnosticDataType"] =
675 "Manager";
676 asyncResp->res
677 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataURI"] =
678 "/redfish/v1/Managers/bmc/LogServices/Dump/"
679 "attachment/" +
680 entryID;
681 }
682 else if (dumpType == "System")
683 {
684 asyncResp->res
685 .jsonValue["Oem"]["OpenBmc"]["DiagnosticDataType"] =
686 "OEM";
687 asyncResp->res
688 .jsonValue["Oem"]["OpenBmc"]["OEMDiagnosticDataType"] =
689 "System";
690 asyncResp->res
691 .jsonValue["Oem"]["OpenBmc"]["AdditionalDataURI"] =
692 "/redfish/v1/Systems/system/LogServices/Dump/"
693 "attachment/" +
694 entryID;
695 }
696 }
697 },
698 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
699 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
700}
701
702inline void deleteDumpEntry(crow::Response& res, const std::string& entryID)
703{
704 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
705
706 auto respHandler = [asyncResp](const boost::system::error_code ec) {
707 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
708 if (ec)
709 {
710 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
711 << ec;
712 messages::internalError(asyncResp->res);
713 return;
714 }
715 };
716 crow::connections::systemBus->async_method_call(
717 respHandler, "xyz.openbmc_project.Dump.Manager",
718 "/xyz/openbmc_project/dump/entry/" + entryID,
719 "xyz.openbmc_project.Object.Delete", "Delete");
720}
721
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500722inline void createDumpTaskCallback(const crow::Request& req,
723 std::shared_ptr<AsyncResp> asyncResp,
724 const uint32_t& dumpId,
725 const std::string& dumpPath,
726 const std::string& dumpType)
727{
728 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
729 [dumpId, dumpPath, dumpType](
730 boost::system::error_code err, sdbusplus::message::message& m,
731 const std::shared_ptr<task::TaskData>& taskData) {
732 std::vector<std::pair<
733 std::string,
734 std::vector<std::pair<std::string, std::variant<std::string>>>>>
735 interfacesList;
736
737 sdbusplus::message::object_path objPath;
738
739 m.read(objPath, interfacesList);
740
741 for (auto& interface : interfacesList)
742 {
743 if (interface.first ==
744 ("xyz.openbmc_project.Dump.Entry." + dumpType))
745 {
746 nlohmann::json retMessage = messages::success();
747 taskData->messages.emplace_back(retMessage);
748
749 std::string headerLoc =
750 "Location: " + dumpPath + std::to_string(dumpId);
751 taskData->payload->httpHeaders.emplace_back(
752 std::move(headerLoc));
753
754 taskData->state = "Completed";
755 return task::completed;
756 }
757 }
758 return !task::completed;
759 },
760 "type='signal',interface='org.freedesktop.DBus."
761 "ObjectManager',"
762 "member='InterfacesAdded', "
763 "path='/xyz/openbmc_project/dump'");
764
765 task->startTimer(std::chrono::minutes(3));
766 task->populateResp(asyncResp->res);
767 task->payload.emplace(req);
768}
769
770inline void createDump(crow::Response& res, const crow::Request& req,
771 const std::string& dumpType)
772{
773 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
774
775 std::string dumpPath;
776 if (dumpType == "BMC")
777 {
778 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
779 }
780 else if (dumpType == "System")
781 {
782 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
783 }
784 else
785 {
786 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
787 messages::internalError(asyncResp->res);
788 return;
789 }
790
791 std::optional<std::string> diagnosticDataType;
792 std::optional<std::string> oemDiagnosticDataType;
793
794 if (!redfish::json_util::readJson(
795 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
796 "OEMDiagnosticDataType", oemDiagnosticDataType))
797 {
798 return;
799 }
800
801 if (dumpType == "System")
802 {
803 if (!oemDiagnosticDataType || !diagnosticDataType)
804 {
805 BMCWEB_LOG_ERROR << "CreateDump action parameter "
806 "'DiagnosticDataType'/"
807 "'OEMDiagnosticDataType' value not found!";
808 messages::actionParameterMissing(
809 asyncResp->res, "CollectDiagnosticData",
810 "DiagnosticDataType & OEMDiagnosticDataType");
811 return;
812 }
813 else if ((*oemDiagnosticDataType != "System") ||
814 (*diagnosticDataType != "OEM"))
815 {
816 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
817 messages::invalidObject(asyncResp->res,
818 "System Dump creation parameters");
819 return;
820 }
821 }
822 else if (dumpType == "BMC")
823 {
824 if (!diagnosticDataType)
825 {
826 BMCWEB_LOG_ERROR << "CreateDump action parameter "
827 "'DiagnosticDataType' not found!";
828 messages::actionParameterMissing(
829 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
830 return;
831 }
832 else if (*diagnosticDataType != "Manager")
833 {
834 BMCWEB_LOG_ERROR
835 << "Wrong parameter value passed for 'DiagnosticDataType'";
836 messages::invalidObject(asyncResp->res,
837 "BMC Dump creation parameters");
838 return;
839 }
840 }
841
842 crow::connections::systemBus->async_method_call(
843 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
844 const uint32_t& dumpId) {
845 if (ec)
846 {
847 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
848 messages::internalError(asyncResp->res);
849 return;
850 }
851 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
852
853 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
854 },
855 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
856 "xyz.openbmc_project.Dump.Create", "CreateDump");
857}
858
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500859inline void clearDump(crow::Response& res, const std::string& dumpInterface)
860{
861 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
862 crow::connections::systemBus->async_method_call(
863 [asyncResp](const boost::system::error_code ec,
864 const std::vector<std::string>& subTreePaths) {
865 if (ec)
866 {
867 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
868 messages::internalError(asyncResp->res);
869 return;
870 }
871
872 for (const std::string& path : subTreePaths)
873 {
874 std::size_t pos = path.rfind("/");
875 if (pos != std::string::npos)
876 {
877 std::string logID = path.substr(pos + 1);
878 deleteDumpEntry(asyncResp->res, logID);
879 }
880 }
881 },
882 "xyz.openbmc_project.ObjectMapper",
883 "/xyz/openbmc_project/object_mapper",
884 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
885 "/xyz/openbmc_project/dump", 0,
886 std::array<std::string, 1>{dumpInterface});
887}
888
Johnathan Mantey043a0532020-03-10 17:15:28 -0700889static void ParseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500890 const std::vector<std::pair<std::string, VariantType>>& params,
891 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700892{
893 for (auto property : params)
894 {
895 if (property.first == "Timestamp")
896 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500897 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500898 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700899 if (value != nullptr)
900 {
901 timestamp = *value;
902 }
903 }
904 else if (property.first == "Filename")
905 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500906 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500907 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700908 if (value != nullptr)
909 {
910 filename = *value;
911 }
912 }
913 else if (property.first == "Log")
914 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500915 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500916 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700917 if (value != nullptr)
918 {
919 logfile = *value;
920 }
921 }
922 }
923}
924
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500925constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800926class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700927{
928 public:
929 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500930 SystemLogServiceCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800931 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800932 {
933 entityPrivileges = {
934 {boost::beast::http::verb::get, {{"Login"}}},
935 {boost::beast::http::verb::head, {{"Login"}}},
936 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
937 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
938 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
939 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
940 }
941
942 private:
943 /**
944 * Functions triggers appropriate requests on DBus
945 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500946 void doGet(crow::Response& res, const crow::Request& req,
947 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800948 {
949 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800950 // Collections don't include the static data added by SubRoute because
951 // it has a duplicate entry for members
952 asyncResp->res.jsonValue["@odata.type"] =
953 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800954 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800955 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800956 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
957 asyncResp->res.jsonValue["Description"] =
958 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500959 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800960 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800961 logServiceArray.push_back(
962 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500963#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
raviteja-bc9bb6862020-02-03 11:53:32 -0600964 logServiceArray.push_back(
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500965 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600966#endif
967
Jason M. Billsd53dd412019-02-12 17:16:22 -0800968#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
969 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500970 {{"@odata.id",
971 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800972#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800973 asyncResp->res.jsonValue["Members@odata.count"] =
974 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800975
976 crow::connections::systemBus->async_method_call(
977 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500978 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800979 if (ec)
980 {
981 BMCWEB_LOG_ERROR << ec;
982 return;
983 }
984
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500985 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800986 {
987 if (pathStr.find("PostCode") != std::string::npos)
988 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500989 nlohmann::json& logServiceArray =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800990 asyncResp->res.jsonValue["Members"];
991 logServiceArray.push_back(
992 {{"@odata.id", "/redfish/v1/Systems/system/"
993 "LogServices/PostCodes"}});
994 asyncResp->res.jsonValue["Members@odata.count"] =
995 logServiceArray.size();
996 return;
997 }
998 }
999 },
1000 "xyz.openbmc_project.ObjectMapper",
1001 "/xyz/openbmc_project/object_mapper",
1002 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001003 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001004 }
1005};
1006
1007class EventLogService : public Node
1008{
1009 public:
1010 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001011 EventLogService(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001012 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001013 {
1014 entityPrivileges = {
1015 {boost::beast::http::verb::get, {{"Login"}}},
1016 {boost::beast::http::verb::head, {{"Login"}}},
1017 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1018 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1019 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1020 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1021 }
1022
1023 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001024 void doGet(crow::Response& res, const crow::Request& req,
1025 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001026 {
1027 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1028
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001029 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001030 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001031 asyncResp->res.jsonValue["@odata.type"] =
1032 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001033 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1034 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -05001035 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001036 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1037 asyncResp->res.jsonValue["Entries"] = {
1038 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -08001039 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -05001040 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1041
1042 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1043 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -07001044 }
1045};
1046
Tim Lee1f56a3a2019-10-09 10:17:57 +08001047class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -07001048{
1049 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001050 JournalEventLogClear(CrowApp& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -07001051 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1052 "LogService.ClearLog/")
1053 {
1054 entityPrivileges = {
1055 {boost::beast::http::verb::get, {{"Login"}}},
1056 {boost::beast::http::verb::head, {{"Login"}}},
1057 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1058 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1059 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1060 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1061 }
1062
1063 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001064 void doPost(crow::Response& res, const crow::Request& req,
1065 const std::vector<std::string>& params) override
Jason M. Bills489640c2019-05-17 09:56:36 -07001066 {
1067 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1068
1069 // Clear the EventLog by deleting the log files
1070 std::vector<std::filesystem::path> redfishLogFiles;
1071 if (getRedfishLogFiles(redfishLogFiles))
1072 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001073 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -07001074 {
1075 std::error_code ec;
1076 std::filesystem::remove(file, ec);
1077 }
1078 }
1079
1080 // Reload rsyslog so it knows to start new log files
1081 crow::connections::systemBus->async_method_call(
1082 [asyncResp](const boost::system::error_code ec) {
1083 if (ec)
1084 {
1085 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1086 messages::internalError(asyncResp->res);
1087 return;
1088 }
1089
1090 messages::success(asyncResp->res);
1091 },
1092 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1093 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1094 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001095 }
1096};
1097
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001098static int fillEventLogEntryJson(const std::string& logEntryID,
Jason M. Bills95820182019-04-22 16:25:34 -07001099 const std::string logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001100 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001101{
Jason M. Bills95820182019-04-22 16:25:34 -07001102 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001103 // First get the Timestamp
1104 size_t space = logEntry.find_first_of(" ");
1105 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001106 {
1107 return 1;
1108 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001109 std::string timestamp = logEntry.substr(0, space);
1110 // Then get the log contents
1111 size_t entryStart = logEntry.find_first_not_of(" ", space);
1112 if (entryStart == std::string::npos)
1113 {
1114 return 1;
1115 }
1116 std::string_view entry(logEntry);
1117 entry.remove_prefix(entryStart);
1118 // Use split to separate the entry into its fields
1119 std::vector<std::string> logEntryFields;
1120 boost::split(logEntryFields, entry, boost::is_any_of(","),
1121 boost::token_compress_on);
1122 // We need at least a MessageId to be valid
1123 if (logEntryFields.size() < 1)
1124 {
1125 return 1;
1126 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001127 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001128
Jason M. Bills4851d452019-03-28 11:27:48 -07001129 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001130 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001131 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001132
Jason M. Bills4851d452019-03-28 11:27:48 -07001133 std::string msg;
1134 std::string severity;
1135 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001136 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001137 msg = message->message;
1138 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001139 }
1140
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001141 // Get the MessageArgs from the log if there are any
1142 boost::beast::span<std::string> messageArgs;
1143 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001144 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001145 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001146 // If the first string is empty, assume there are no MessageArgs
1147 std::size_t messageArgsSize = 0;
1148 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001149 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001150 messageArgsSize = logEntryFields.size() - 1;
1151 }
1152
1153 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
1154
1155 // Fill the MessageArgs into the Message
1156 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001157 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001158 {
1159 std::string argStr = "%" + std::to_string(++i);
1160 size_t argPos = msg.find(argStr);
1161 if (argPos != std::string::npos)
1162 {
1163 msg.replace(argPos, argStr.length(), messageArg);
1164 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001165 }
1166 }
1167
Jason M. Bills95820182019-04-22 16:25:34 -07001168 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1169 // format which matches the Redfish format except for the fractional seconds
1170 // between the '.' and the '+', so just remove them.
1171 std::size_t dot = timestamp.find_first_of(".");
1172 std::size_t plus = timestamp.find_first_of("+");
1173 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001174 {
Jason M. Bills95820182019-04-22 16:25:34 -07001175 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001176 }
1177
1178 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001179 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001180 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001181 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001182 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001183 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001184 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001185 {"Id", logEntryID},
1186 {"Message", std::move(msg)},
1187 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001188 {"MessageArgs", std::move(messageArgs)},
1189 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001190 {"Severity", std::move(severity)},
1191 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001192 return 0;
1193}
1194
Anthony Wilson27062602019-04-22 02:10:09 -05001195class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001196{
1197 public:
1198 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001199 JournalEventLogEntryCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -08001200 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001201 {
1202 entityPrivileges = {
1203 {boost::beast::http::verb::get, {{"Login"}}},
1204 {boost::beast::http::verb::head, {{"Login"}}},
1205 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1206 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1207 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1208 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1209 }
1210
1211 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001212 void doGet(crow::Response& res, const crow::Request& req,
1213 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001214 {
1215 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -07001216 uint64_t skip = 0;
1217 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001218 if (!getSkipParam(asyncResp->res, req, skip))
1219 {
1220 return;
1221 }
1222 if (!getTopParam(asyncResp->res, req, top))
1223 {
1224 return;
1225 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001226 // Collections don't include the static data added by SubRoute because
1227 // it has a duplicate entry for members
1228 asyncResp->res.jsonValue["@odata.type"] =
1229 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001230 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -08001231 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001232 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1233 asyncResp->res.jsonValue["Description"] =
1234 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -07001235
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001236 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001237 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -07001238 // Go through the log files and create a unique ID for each entry
1239 std::vector<std::filesystem::path> redfishLogFiles;
1240 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +00001241 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -07001242 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -07001243
1244 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -07001245 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1246 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001247 {
Jason M. Billscd225da2019-05-08 15:31:57 -07001248 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -07001249 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001250 {
1251 continue;
1252 }
1253
Jason M. Billse85d6b12019-07-29 17:01:15 -07001254 // Reset the unique ID on the first entry
1255 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -07001256 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001257 {
Jason M. Bills95820182019-04-22 16:25:34 -07001258 entryCount++;
1259 // Handle paging using skip (number of entries to skip from the
1260 // start) and top (number of entries to display)
1261 if (entryCount <= skip || entryCount > skip + top)
1262 {
1263 continue;
1264 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001265
Jason M. Bills95820182019-04-22 16:25:34 -07001266 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001267 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -07001268 {
1269 continue;
1270 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001271
Jason M. Billse85d6b12019-07-29 17:01:15 -07001272 if (firstEntry)
1273 {
1274 firstEntry = false;
1275 }
1276
Jason M. Bills95820182019-04-22 16:25:34 -07001277 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001278 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -07001279 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1280 {
1281 messages::internalError(asyncResp->res);
1282 return;
1283 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001284 }
1285 }
1286 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1287 if (skip + top < entryCount)
1288 {
1289 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -07001290 "/redfish/v1/Systems/system/LogServices/EventLog/"
1291 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001292 std::to_string(skip + top);
1293 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001294 }
1295};
1296
Jason M. Bills897967d2019-07-29 17:05:30 -07001297class JournalEventLogEntry : public Node
1298{
1299 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001300 JournalEventLogEntry(CrowApp& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -07001301 Node(app,
1302 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1303 std::string())
1304 {
1305 entityPrivileges = {
1306 {boost::beast::http::verb::get, {{"Login"}}},
1307 {boost::beast::http::verb::head, {{"Login"}}},
1308 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1309 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1310 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1311 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1312 }
1313
1314 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001315 void doGet(crow::Response& res, const crow::Request& req,
1316 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -07001317 {
1318 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1319 if (params.size() != 1)
1320 {
1321 messages::internalError(asyncResp->res);
1322 return;
1323 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001324 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -07001325
1326 // Go through the log files and check the unique ID for each entry to
1327 // find the target entry
1328 std::vector<std::filesystem::path> redfishLogFiles;
1329 getRedfishLogFiles(redfishLogFiles);
1330 std::string logEntry;
1331
1332 // Oldest logs are in the last file, so start there and loop backwards
1333 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1334 it++)
1335 {
1336 std::ifstream logStream(*it);
1337 if (!logStream.is_open())
1338 {
1339 continue;
1340 }
1341
1342 // Reset the unique ID on the first entry
1343 bool firstEntry = true;
1344 while (std::getline(logStream, logEntry))
1345 {
1346 std::string idStr;
1347 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1348 {
1349 continue;
1350 }
1351
1352 if (firstEntry)
1353 {
1354 firstEntry = false;
1355 }
1356
1357 if (idStr == targetID)
1358 {
1359 if (fillEventLogEntryJson(idStr, logEntry,
1360 asyncResp->res.jsonValue) != 0)
1361 {
1362 messages::internalError(asyncResp->res);
1363 return;
1364 }
1365 return;
1366 }
1367 }
1368 }
1369 // Requested ID was not found
1370 messages::resourceMissingAtURI(asyncResp->res, targetID);
1371 }
1372};
1373
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001374class DBusEventLogEntryCollection : public Node
1375{
1376 public:
1377 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001378 DBusEventLogEntryCollection(CrowApp& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001379 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
1380 {
1381 entityPrivileges = {
1382 {boost::beast::http::verb::get, {{"Login"}}},
1383 {boost::beast::http::verb::head, {{"Login"}}},
1384 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1385 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1386 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1387 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1388 }
1389
1390 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001391 void doGet(crow::Response& res, const crow::Request& req,
1392 const std::vector<std::string>& params) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001393 {
1394 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1395
1396 // Collections don't include the static data added by SubRoute because
1397 // it has a duplicate entry for members
1398 asyncResp->res.jsonValue["@odata.type"] =
1399 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001400 asyncResp->res.jsonValue["@odata.id"] =
1401 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1402 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1403 asyncResp->res.jsonValue["Description"] =
1404 "Collection of System Event Log Entries";
1405
Andrew Geisslercb92c032018-08-17 07:56:14 -07001406 // DBus implementation of EventLog/Entries
1407 // Make call to Logging Service to find all log entry objects
1408 crow::connections::systemBus->async_method_call(
1409 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001410 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001411 if (ec)
1412 {
1413 // TODO Handle for specific error code
1414 BMCWEB_LOG_ERROR
1415 << "getLogEntriesIfaceData resp_handler got error "
1416 << ec;
1417 messages::internalError(asyncResp->res);
1418 return;
1419 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001420 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001421 asyncResp->res.jsonValue["Members"];
1422 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001423 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001424 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001425 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001426 {
1427 if (interfaceMap.first !=
1428 "xyz.openbmc_project.Logging.Entry")
1429 {
1430 BMCWEB_LOG_DEBUG << "Bailing early on "
1431 << interfaceMap.first;
1432 continue;
1433 }
1434 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001435 nlohmann::json& thisEntry = entriesArray.back();
1436 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001437 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001438 std::string* severity = nullptr;
1439 std::string* message = nullptr;
1440 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001441 {
1442 if (propertyMap.first == "Id")
1443 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001444 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001445 if (id == nullptr)
1446 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001447 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001448 }
1449 }
1450 else if (propertyMap.first == "Timestamp")
1451 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001452 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001453 std::get_if<uint64_t>(&propertyMap.second);
1454 if (millisTimeStamp == nullptr)
1455 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001456 messages::internalError(asyncResp->res);
Ed Tanous271584a2019-07-09 16:24:22 -07001457 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001458 }
1459 // Retrieve Created property with format:
1460 // yyyy-mm-ddThh:mm:ss
1461 std::chrono::milliseconds chronoTimeStamp(
1462 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001463 timestamp = std::chrono::duration_cast<
1464 std::chrono::duration<int>>(
1465 chronoTimeStamp)
1466 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001467 }
1468 else if (propertyMap.first == "Severity")
1469 {
1470 severity = std::get_if<std::string>(
1471 &propertyMap.second);
1472 if (severity == nullptr)
1473 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001474 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001475 }
1476 }
1477 else if (propertyMap.first == "Message")
1478 {
1479 message = std::get_if<std::string>(
1480 &propertyMap.second);
1481 if (message == nullptr)
1482 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001483 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001484 }
1485 }
1486 }
1487 thisEntry = {
1488 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001489 {"@odata.id",
1490 "/redfish/v1/Systems/system/LogServices/EventLog/"
1491 "Entries/" +
1492 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001493 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001494 {"Id", std::to_string(*id)},
1495 {"Message", *message},
1496 {"EntryType", "Event"},
1497 {"Severity",
1498 translateSeverityDbusToRedfish(*severity)},
1499 {"Created", crow::utility::getDateTime(timestamp)}};
1500 }
1501 }
1502 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001503 [](const nlohmann::json& left,
1504 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001505 return (left["Id"] <= right["Id"]);
1506 });
1507 asyncResp->res.jsonValue["Members@odata.count"] =
1508 entriesArray.size();
1509 },
1510 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1511 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001512 }
1513};
1514
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001515class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001516{
1517 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001518 DBusEventLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001519 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001520 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1521 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001522 {
1523 entityPrivileges = {
1524 {boost::beast::http::verb::get, {{"Login"}}},
1525 {boost::beast::http::verb::head, {{"Login"}}},
1526 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1527 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1528 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1529 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1530 }
1531
1532 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001533 void doGet(crow::Response& res, const crow::Request& req,
1534 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001535 {
1536 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001537 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001538 {
1539 messages::internalError(asyncResp->res);
1540 return;
1541 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001542 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001543
Andrew Geisslercb92c032018-08-17 07:56:14 -07001544 // DBus implementation of EventLog/Entries
1545 // Make call to Logging Service to find all log entry objects
1546 crow::connections::systemBus->async_method_call(
1547 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001548 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001549 if (ec)
1550 {
1551 BMCWEB_LOG_ERROR
1552 << "EventLogEntry (DBus) resp_handler got error " << ec;
1553 messages::internalError(asyncResp->res);
1554 return;
1555 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001556 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001557 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001558 std::string* severity = nullptr;
1559 std::string* message = nullptr;
1560 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001561 {
1562 if (propertyMap.first == "Id")
1563 {
1564 id = std::get_if<uint32_t>(&propertyMap.second);
1565 if (id == nullptr)
1566 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001567 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001568 }
1569 }
1570 else if (propertyMap.first == "Timestamp")
1571 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001572 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001573 std::get_if<uint64_t>(&propertyMap.second);
1574 if (millisTimeStamp == nullptr)
1575 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001576 messages::internalError(asyncResp->res);
Ed Tanous271584a2019-07-09 16:24:22 -07001577 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001578 }
1579 // Retrieve Created property with format:
1580 // yyyy-mm-ddThh:mm:ss
1581 std::chrono::milliseconds chronoTimeStamp(
1582 *millisTimeStamp);
1583 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001584 std::chrono::duration_cast<
1585 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001586 .count();
1587 }
1588 else if (propertyMap.first == "Severity")
1589 {
1590 severity =
1591 std::get_if<std::string>(&propertyMap.second);
1592 if (severity == nullptr)
1593 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001594 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001595 }
1596 }
1597 else if (propertyMap.first == "Message")
1598 {
1599 message = std::get_if<std::string>(&propertyMap.second);
1600 if (message == nullptr)
1601 {
Asmitha Karunanithi0f1d8ed2020-08-02 11:11:47 -05001602 messages::internalError(asyncResp->res);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001603 }
1604 }
1605 }
Ed Tanous271584a2019-07-09 16:24:22 -07001606 if (id == nullptr || message == nullptr || severity == nullptr)
1607 {
1608 return;
1609 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001610 asyncResp->res.jsonValue = {
1611 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001612 {"@odata.id",
1613 "/redfish/v1/Systems/system/LogServices/EventLog/"
1614 "Entries/" +
1615 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001616 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001617 {"Id", std::to_string(*id)},
1618 {"Message", *message},
1619 {"EntryType", "Event"},
1620 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001621 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001622 },
1623 "xyz.openbmc_project.Logging",
1624 "/xyz/openbmc_project/logging/entry/" + entryID,
1625 "org.freedesktop.DBus.Properties", "GetAll",
1626 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001627 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001628
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001629 void doDelete(crow::Response& res, const crow::Request& req,
1630 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001631 {
1632
1633 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1634
1635 auto asyncResp = std::make_shared<AsyncResp>(res);
1636
1637 if (params.size() != 1)
1638 {
1639 messages::internalError(asyncResp->res);
1640 return;
1641 }
1642 std::string entryID = params[0];
1643
1644 dbus::utility::escapePathForDbus(entryID);
1645
1646 // Process response from Logging service.
1647 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1648 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1649 if (ec)
1650 {
1651 // TODO Handle for specific error code
1652 BMCWEB_LOG_ERROR
1653 << "EventLogEntry (DBus) doDelete respHandler got error "
1654 << ec;
1655 asyncResp->res.result(
1656 boost::beast::http::status::internal_server_error);
1657 return;
1658 }
1659
1660 asyncResp->res.result(boost::beast::http::status::ok);
1661 };
1662
1663 // Make call to Logging service to request Delete Log
1664 crow::connections::systemBus->async_method_call(
1665 respHandler, "xyz.openbmc_project.Logging",
1666 "/xyz/openbmc_project/logging/entry/" + entryID,
1667 "xyz.openbmc_project.Object.Delete", "Delete");
1668 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001669};
1670
1671class BMCLogServiceCollection : public Node
1672{
1673 public:
1674 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001675 BMCLogServiceCollection(CrowApp& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001676 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001677 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001678 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001679 {boost::beast::http::verb::get, {{"Login"}}},
1680 {boost::beast::http::verb::head, {{"Login"}}},
1681 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1682 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1683 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1684 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001685 }
1686
1687 private:
1688 /**
1689 * Functions triggers appropriate requests on DBus
1690 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001691 void doGet(crow::Response& res, const crow::Request& req,
1692 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001693 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001694 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001695 // Collections don't include the static data added by SubRoute because
1696 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001697 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001698 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001699 asyncResp->res.jsonValue["@odata.id"] =
1700 "/redfish/v1/Managers/bmc/LogServices";
1701 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1702 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001703 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001704 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001705 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001706#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1707 logServiceArray.push_back(
1708 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
1709#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001710#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1711 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001712 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001713#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001714 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001715 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001716 }
1717};
1718
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001719class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001720{
1721 public:
1722 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001723 BMCJournalLogService(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001724 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001725 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001726 entityPrivileges = {
1727 {boost::beast::http::verb::get, {{"Login"}}},
1728 {boost::beast::http::verb::head, {{"Login"}}},
1729 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1730 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1731 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1732 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1733 }
1734
1735 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001736 void doGet(crow::Response& res, const crow::Request& req,
1737 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001738 {
1739 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001740 asyncResp->res.jsonValue["@odata.type"] =
1741 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001742 asyncResp->res.jsonValue["@odata.id"] =
1743 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001744 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1745 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1746 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001747 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001748 asyncResp->res.jsonValue["Entries"] = {
1749 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001750 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001751 }
1752};
1753
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001754static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1755 sd_journal* journal,
1756 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001757{
1758 // Get the Log Entry contents
1759 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001760
Ed Tanous39e77502019-03-04 17:35:53 -08001761 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001762 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001763 if (ret < 0)
1764 {
1765 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1766 return 1;
1767 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001768
1769 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001770 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001771 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001772 if (ret < 0)
1773 {
1774 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001775 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001776
1777 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001778 std::string entryTimeStr;
1779 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001780 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001781 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001782 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001783
1784 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001785 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001786 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001787 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1788 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001789 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001790 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001791 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001792 {"EntryType", "Oem"},
1793 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001794 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001795 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001796 {"Created", std::move(entryTimeStr)}};
1797 return 0;
1798}
1799
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001800class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001801{
1802 public:
1803 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001804 BMCJournalLogEntryCollection(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001805 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001806 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001807 entityPrivileges = {
1808 {boost::beast::http::verb::get, {{"Login"}}},
1809 {boost::beast::http::verb::head, {{"Login"}}},
1810 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1811 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1812 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1813 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1814 }
1815
1816 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001817 void doGet(crow::Response& res, const crow::Request& req,
1818 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001819 {
1820 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001821 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001822 uint64_t skip = 0;
1823 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001824 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001825 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001826 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001827 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001828 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001829 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001830 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001831 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001832 // Collections don't include the static data added by SubRoute because
1833 // it has a duplicate entry for members
1834 asyncResp->res.jsonValue["@odata.type"] =
1835 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001836 asyncResp->res.jsonValue["@odata.id"] =
1837 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001838 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001839 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001840 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1841 asyncResp->res.jsonValue["Description"] =
1842 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001843 asyncResp->res.jsonValue["@odata.id"] =
1844 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001845 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001846 logEntryArray = nlohmann::json::array();
1847
1848 // Go through the journal and use the timestamp to create a unique ID
1849 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001850 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001851 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1852 if (ret < 0)
1853 {
1854 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001855 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001856 return;
1857 }
1858 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1859 journalTmp, sd_journal_close);
1860 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001861 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001862 // Reset the unique ID on the first entry
1863 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001864 SD_JOURNAL_FOREACH(journal.get())
1865 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001866 entryCount++;
1867 // Handle paging using skip (number of entries to skip from the
1868 // start) and top (number of entries to display)
1869 if (entryCount <= skip || entryCount > skip + top)
1870 {
1871 continue;
1872 }
1873
Jason M. Bills16428a12018-11-02 12:42:29 -07001874 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001875 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001876 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001877 continue;
1878 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001879
Jason M. Billse85d6b12019-07-29 17:01:15 -07001880 if (firstEntry)
1881 {
1882 firstEntry = false;
1883 }
1884
Jason M. Billse1f26342018-07-18 12:12:00 -07001885 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001886 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001887 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1888 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001889 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001890 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001891 return;
1892 }
1893 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001894 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1895 if (skip + top < entryCount)
1896 {
1897 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001898 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001899 std::to_string(skip + top);
1900 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001901 }
1902};
1903
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001904class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001905{
1906 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001907 BMCJournalLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001908 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001909 std::string())
1910 {
1911 entityPrivileges = {
1912 {boost::beast::http::verb::get, {{"Login"}}},
1913 {boost::beast::http::verb::head, {{"Login"}}},
1914 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1915 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1916 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1917 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1918 }
1919
1920 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001921 void doGet(crow::Response& res, const crow::Request& req,
1922 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001923 {
1924 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1925 if (params.size() != 1)
1926 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001927 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001928 return;
1929 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001930 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001931 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001932 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001933 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001934 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001935 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001936 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001937 }
1938
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001939 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001940 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1941 if (ret < 0)
1942 {
1943 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001944 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001945 return;
1946 }
1947 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1948 journalTmp, sd_journal_close);
1949 journalTmp = nullptr;
1950 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001951 // tracking the unique ID
1952 std::string idStr;
1953 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001954 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301955 if (ret < 0)
1956 {
1957 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1958 << strerror(-ret);
1959 messages::internalError(asyncResp->res);
1960 return;
1961 }
Ed Tanous271584a2019-07-09 16:24:22 -07001962 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001963 {
1964 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001965 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1966 {
1967 messages::internalError(asyncResp->res);
1968 return;
1969 }
1970 if (firstEntry)
1971 {
1972 firstEntry = false;
1973 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001974 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001975 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001976 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001977 {
1978 messages::resourceMissingAtURI(asyncResp->res, entryID);
1979 return;
1980 }
1981
1982 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1983 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001984 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001985 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001986 return;
1987 }
1988 }
1989};
1990
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001991class BMCDumpService : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06001992{
1993 public:
1994 template <typename CrowApp>
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001995 BMCDumpService(CrowApp& app) :
1996 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
raviteja-bc9bb6862020-02-03 11:53:32 -06001997 {
1998 entityPrivileges = {
1999 {boost::beast::http::verb::get, {{"Login"}}},
2000 {boost::beast::http::verb::head, {{"Login"}}},
2001 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2002 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2003 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2004 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2005 }
2006
2007 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002008 void doGet(crow::Response& res, const crow::Request& req,
2009 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002010 {
2011 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2012
2013 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002014 "/redfish/v1/Managers/bmc/LogServices/Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002015 asyncResp->res.jsonValue["@odata.type"] =
2016 "#LogService.v1_1_0.LogService";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002017 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2018 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2019 asyncResp->res.jsonValue["Id"] = "Dump";
raviteja-bc9bb6862020-02-03 11:53:32 -06002020 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
raviteja-bc9bb6862020-02-03 11:53:32 -06002021 asyncResp->res.jsonValue["Entries"] = {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002022 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2023 asyncResp->res.jsonValue["Actions"] = {
2024 {"#LogService.ClearLog",
2025 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2026 "Actions/LogService.ClearLog"}}},
2027 {"Oem",
2028 {{"#OemLogService.CollectDiagnosticData",
2029 {{"target",
2030 "/redfish/v1/Managers/bmc/LogServices/Dump/"
2031 "Actions/Oem/OemLogService.CollectDiagnosticData"}}}}}};
raviteja-bc9bb6862020-02-03 11:53:32 -06002032 }
2033};
2034
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002035class BMCDumpEntryCollection : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002036{
2037 public:
2038 template <typename CrowApp>
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002039 BMCDumpEntryCollection(CrowApp& app) :
2040 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
raviteja-bc9bb6862020-02-03 11:53:32 -06002041 {
2042 entityPrivileges = {
2043 {boost::beast::http::verb::get, {{"Login"}}},
2044 {boost::beast::http::verb::head, {{"Login"}}},
2045 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2046 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2047 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2048 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2049 }
2050
2051 private:
2052 /**
2053 * Functions triggers appropriate requests on DBus
2054 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002055 void doGet(crow::Response& res, const crow::Request& req,
2056 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002057 {
2058 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2059
2060 asyncResp->res.jsonValue["@odata.type"] =
2061 "#LogEntryCollection.LogEntryCollection";
2062 asyncResp->res.jsonValue["@odata.id"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002063 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2064 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002065 asyncResp->res.jsonValue["Description"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002066 "Collection of BMC Dump Entries";
raviteja-bc9bb6862020-02-03 11:53:32 -06002067
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002068 getDumpEntryCollection(asyncResp, "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002069 }
2070};
2071
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002072class BMCDumpEntry : public Node
raviteja-bc9bb6862020-02-03 11:53:32 -06002073{
2074 public:
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002075 BMCDumpEntry(CrowApp& app) :
2076 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/",
raviteja-bc9bb6862020-02-03 11:53:32 -06002077 std::string())
2078 {
2079 entityPrivileges = {
2080 {boost::beast::http::verb::get, {{"Login"}}},
2081 {boost::beast::http::verb::head, {{"Login"}}},
2082 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2083 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2084 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2085 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2086 }
2087
2088 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002089 void doGet(crow::Response& res, const crow::Request& req,
2090 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002091 {
2092 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2093 if (params.size() != 1)
2094 {
2095 messages::internalError(asyncResp->res);
2096 return;
2097 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002098 getDumpEntryById(asyncResp, params[0], "BMC");
raviteja-bc9bb6862020-02-03 11:53:32 -06002099 }
2100
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002101 void doDelete(crow::Response& res, const crow::Request& req,
2102 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06002103 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002104 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002105 if (params.size() != 1)
2106 {
2107 messages::internalError(asyncResp->res);
2108 return;
2109 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002110 deleteDumpEntry(asyncResp->res, params[0]);
2111 }
2112};
raviteja-bc9bb6862020-02-03 11:53:32 -06002113
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002114class BMCDumpCreate : public Node
2115{
2116 public:
2117 BMCDumpCreate(CrowApp& app) :
2118 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2119 "Actions/Oem/"
2120 "OemLogService.CollectDiagnosticData/")
2121 {
2122 entityPrivileges = {
2123 {boost::beast::http::verb::get, {{"Login"}}},
2124 {boost::beast::http::verb::head, {{"Login"}}},
2125 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2126 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2127 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2128 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2129 }
2130
2131 private:
2132 void doPost(crow::Response& res, const crow::Request& req,
2133 const std::vector<std::string>& params) override
2134 {
2135 createDump(res, req, "BMC");
2136 }
2137};
2138
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002139class BMCDumpClear : public Node
2140{
2141 public:
2142 BMCDumpClear(CrowApp& app) :
2143 Node(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2144 "Actions/"
2145 "LogService.ClearLog/")
2146 {
2147 entityPrivileges = {
2148 {boost::beast::http::verb::get, {{"Login"}}},
2149 {boost::beast::http::verb::head, {{"Login"}}},
2150 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2151 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2152 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2153 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2154 }
2155
2156 private:
2157 void doPost(crow::Response& res, const crow::Request& req,
2158 const std::vector<std::string>& params) override
2159 {
2160 clearDump(res, "xyz.openbmc_project.Dump.Entry.BMC");
2161 }
2162};
2163
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002164class SystemDumpService : public Node
2165{
2166 public:
2167 template <typename CrowApp>
2168 SystemDumpService(CrowApp& app) :
2169 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/")
2170 {
2171 entityPrivileges = {
2172 {boost::beast::http::verb::get, {{"Login"}}},
2173 {boost::beast::http::verb::head, {{"Login"}}},
2174 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2175 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2176 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2177 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2178 }
raviteja-bc9bb6862020-02-03 11:53:32 -06002179
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002180 private:
2181 void doGet(crow::Response& res, const crow::Request& req,
2182 const std::vector<std::string>& params) override
2183 {
2184 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
raviteja-bc9bb6862020-02-03 11:53:32 -06002185
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002186 asyncResp->res.jsonValue["@odata.id"] =
2187 "/redfish/v1/Systems/system/LogServices/Dump";
2188 asyncResp->res.jsonValue["@odata.type"] =
2189 "#LogService.v1_1_0.LogService";
2190 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2191 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2192 asyncResp->res.jsonValue["Id"] = "Dump";
2193 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2194 asyncResp->res.jsonValue["Entries"] = {
2195 {"@odata.id",
2196 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2197 asyncResp->res.jsonValue["Actions"] = {
2198 {"#LogService.ClearLog",
2199 {{"target", "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2200 "LogService.ClearLog"}}},
2201 {"Oem",
2202 {{"#OemLogService.CollectDiagnosticData",
2203 {{"target",
2204 "/redfish/v1/Systems/system/LogServices/Dump/Actions/Oem/"
2205 "OemLogService.CollectDiagnosticData"}}}}}};
2206 }
2207};
2208
2209class SystemDumpEntryCollection : public Node
2210{
2211 public:
2212 template <typename CrowApp>
2213 SystemDumpEntryCollection(CrowApp& app) :
2214 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
2215 {
2216 entityPrivileges = {
2217 {boost::beast::http::verb::get, {{"Login"}}},
2218 {boost::beast::http::verb::head, {{"Login"}}},
2219 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2220 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2221 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2222 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2223 }
2224
2225 private:
2226 /**
2227 * Functions triggers appropriate requests on DBus
2228 */
2229 void doGet(crow::Response& res, const crow::Request& req,
2230 const std::vector<std::string>& params) override
2231 {
2232 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2233
2234 asyncResp->res.jsonValue["@odata.type"] =
2235 "#LogEntryCollection.LogEntryCollection";
2236 asyncResp->res.jsonValue["@odata.id"] =
2237 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2238 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2239 asyncResp->res.jsonValue["Description"] =
2240 "Collection of System Dump Entries";
2241
2242 getDumpEntryCollection(asyncResp, "System");
2243 }
2244};
2245
2246class SystemDumpEntry : public Node
2247{
2248 public:
2249 SystemDumpEntry(CrowApp& app) :
2250 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/",
2251 std::string())
2252 {
2253 entityPrivileges = {
2254 {boost::beast::http::verb::get, {{"Login"}}},
2255 {boost::beast::http::verb::head, {{"Login"}}},
2256 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2257 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2258 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2259 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2260 }
2261
2262 private:
2263 void doGet(crow::Response& res, const crow::Request& req,
2264 const std::vector<std::string>& params) override
2265 {
2266 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2267 if (params.size() != 1)
2268 {
2269 messages::internalError(asyncResp->res);
2270 return;
2271 }
2272 getDumpEntryById(asyncResp, params[0], "System");
2273 }
2274
2275 void doDelete(crow::Response& res, const crow::Request& req,
2276 const std::vector<std::string>& params) override
2277 {
2278 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2279 if (params.size() != 1)
2280 {
2281 messages::internalError(asyncResp->res);
2282 return;
2283 }
2284 deleteDumpEntry(asyncResp->res, params[0]);
raviteja-bc9bb6862020-02-03 11:53:32 -06002285 }
2286};
2287
Asmitha Karunanithia43be802020-05-07 05:05:36 -05002288class SystemDumpCreate : public Node
2289{
2290 public:
2291 SystemDumpCreate(CrowApp& app) :
2292 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2293 "Actions/Oem/"
2294 "OemLogService.CollectDiagnosticData/")
2295 {
2296 entityPrivileges = {
2297 {boost::beast::http::verb::get, {{"Login"}}},
2298 {boost::beast::http::verb::head, {{"Login"}}},
2299 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2300 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2301 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2302 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2303 }
2304
2305 private:
2306 void doPost(crow::Response& res, const crow::Request& req,
2307 const std::vector<std::string>& params) override
2308 {
2309 createDump(res, req, "System");
2310 }
2311};
2312
raviteja-b06578432020-02-03 12:50:42 -06002313class SystemDumpEntryDownload : public Node
2314{
2315 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002316 SystemDumpEntryDownload(CrowApp& app) :
raviteja-b06578432020-02-03 12:50:42 -06002317 Node(app,
2318 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/"
2319 "Actions/"
2320 "LogEntry.DownloadLog/",
2321 std::string())
2322 {
2323 entityPrivileges = {
2324 {boost::beast::http::verb::get, {{"Login"}}},
2325 {boost::beast::http::verb::head, {{"Login"}}},
2326 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2327 }
2328
2329 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002330 void doPost(crow::Response& res, const crow::Request& req,
2331 const std::vector<std::string>& params) override
raviteja-b06578432020-02-03 12:50:42 -06002332 {
2333 if (params.size() != 1)
2334 {
2335 messages::internalError(res);
2336 return;
2337 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002338 const std::string& entryID = params[0];
raviteja-b06578432020-02-03 12:50:42 -06002339 crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID);
2340 }
2341};
2342
raviteja-b013487e2020-03-03 03:20:48 -06002343class SystemDumpClear : public Node
2344{
2345 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002346 SystemDumpClear(CrowApp& app) :
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002347 Node(app, "/redfish/v1/Systems/system/LogServices/Dump/"
raviteja-b013487e2020-03-03 03:20:48 -06002348 "Actions/"
2349 "LogService.ClearLog/")
2350 {
2351 entityPrivileges = {
2352 {boost::beast::http::verb::get, {{"Login"}}},
2353 {boost::beast::http::verb::head, {{"Login"}}},
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002354 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2355 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2356 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
raviteja-b013487e2020-03-03 03:20:48 -06002357 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2358 }
2359
2360 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002361 void doPost(crow::Response& res, const crow::Request& req,
2362 const std::vector<std::string>& params) override
raviteja-b013487e2020-03-03 03:20:48 -06002363 {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -05002364 clearDump(res, "xyz.openbmc_project.Dump.Entry.System");
raviteja-b013487e2020-03-03 03:20:48 -06002365 }
2366};
2367
Jason M. Bills424c4172019-03-21 13:50:33 -07002368class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07002369{
2370 public:
2371 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002372 CrashdumpService(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002373 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002374 {
AppaRao Puli39460282020-04-07 17:03:04 +05302375 // Note: Deviated from redfish privilege registry for GET & HEAD
2376 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002377 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302378 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2379 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002380 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2381 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2382 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2383 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002384 }
2385
2386 private:
2387 /**
2388 * Functions triggers appropriate requests on DBus
2389 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002390 void doGet(crow::Response& res, const crow::Request& req,
2391 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002392 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002393 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002394 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08002395 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002396 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002397 asyncResp->res.jsonValue["@odata.type"] =
2398 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06002399 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2400 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2401 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07002402 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2403 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08002404 asyncResp->res.jsonValue["Entries"] = {
2405 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07002406 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07002407 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002408 {"#LogService.ClearLog",
2409 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2410 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07002411 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07002412 {{"#Crashdump.OnDemand",
2413 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002414 "Actions/Oem/Crashdump.OnDemand"}}},
2415 {"#Crashdump.Telemetry",
2416 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2417 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002418
2419#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07002420 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07002421 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05002422 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2423 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07002424#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07002425 }
2426};
2427
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002428class CrashdumpClear : public Node
2429{
2430 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002431 CrashdumpClear(CrowApp& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002432 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2433 "LogService.ClearLog/")
2434 {
AppaRao Puli39460282020-04-07 17:03:04 +05302435 // Note: Deviated from redfish privilege registry for GET & HEAD
2436 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002437 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302438 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2439 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002440 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2441 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2442 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2443 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2444 }
2445
2446 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002447 void doPost(crow::Response& res, const crow::Request& req,
2448 const std::vector<std::string>& params) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002449 {
2450 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2451
2452 crow::connections::systemBus->async_method_call(
2453 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002454 const std::string& resp) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002455 if (ec)
2456 {
2457 messages::internalError(asyncResp->res);
2458 return;
2459 }
2460 messages::success(asyncResp->res);
2461 },
2462 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2463 }
2464};
2465
Jason M. Billse855dd22019-10-08 11:37:48 -07002466static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002467 const std::string& logID,
2468 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002469{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002470 auto getStoredLogCallback =
2471 [asyncResp, logID, &logEntryJson](
2472 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002473 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002474 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002475 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002476 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2477 if (ec.value() ==
2478 boost::system::linux_error::bad_request_descriptor)
2479 {
2480 messages::resourceNotFound(asyncResp->res, "LogEntry",
2481 logID);
2482 }
2483 else
2484 {
2485 messages::internalError(asyncResp->res);
2486 }
2487 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002488 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002489
Johnathan Mantey043a0532020-03-10 17:15:28 -07002490 std::string timestamp{};
2491 std::string filename{};
2492 std::string logfile{};
2493 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2494
2495 if (filename.empty() || timestamp.empty())
2496 {
2497 messages::resourceMissingAtURI(asyncResp->res, logID);
2498 return;
2499 }
2500
2501 std::string crashdumpURI =
2502 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2503 logID + "/" + filename;
2504 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2505 {"@odata.id", "/redfish/v1/Systems/system/"
2506 "LogServices/Crashdump/Entries/" +
2507 logID},
2508 {"Name", "CPU Crashdump"},
2509 {"Id", logID},
2510 {"EntryType", "Oem"},
2511 {"OemRecordFormat", "Crashdump URI"},
2512 {"Message", std::move(crashdumpURI)},
2513 {"Created", std::move(timestamp)}};
2514 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002515 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002516 std::move(getStoredLogCallback), crashdumpObject,
2517 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002518 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002519}
2520
Jason M. Bills424c4172019-03-21 13:50:33 -07002521class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002522{
2523 public:
2524 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002525 CrashdumpEntryCollection(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002526 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002527 {
AppaRao Puli39460282020-04-07 17:03:04 +05302528 // Note: Deviated from redfish privilege registry for GET & HEAD
2529 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002530 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302531 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2532 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002533 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2534 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2535 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2536 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002537 }
2538
2539 private:
2540 /**
2541 * Functions triggers appropriate requests on DBus
2542 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002543 void doGet(crow::Response& res, const crow::Request& req,
2544 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002545 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002546 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002547 // Collections don't include the static data added by SubRoute because
2548 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002549 auto getLogEntriesCallback = [asyncResp](
2550 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002551 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002552 if (ec)
2553 {
2554 if (ec.value() !=
2555 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002556 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002557 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2558 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002559 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002560 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002561 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002562 }
2563 asyncResp->res.jsonValue["@odata.type"] =
2564 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002565 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002566 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002567 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002568 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002569 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002570 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002571 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002572 std::vector<std::string> logIDs;
2573 // Get the list of log entries and build up an empty array big
2574 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002575 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002576 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002577 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002578 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002579 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002580 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002581 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002582 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002583 logIDs.emplace_back(objpath.substr(lastPos + 1));
2584
2585 // Add a space for the log entry to the array
2586 logEntryArray.push_back({});
2587 }
2588 // Now go through and set up async calls to fill in the entries
2589 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002590 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002591 {
2592 // Add the log entry to the array
2593 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002594 }
2595 asyncResp->res.jsonValue["Members@odata.count"] =
2596 logEntryArray.size();
2597 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002598 crow::connections::systemBus->async_method_call(
2599 std::move(getLogEntriesCallback),
2600 "xyz.openbmc_project.ObjectMapper",
2601 "/xyz/openbmc_project/object_mapper",
2602 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002603 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002604 }
2605};
2606
Jason M. Bills424c4172019-03-21 13:50:33 -07002607class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002608{
2609 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002610 CrashdumpEntry(CrowApp& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002611 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002612 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002613 std::string())
2614 {
AppaRao Puli39460282020-04-07 17:03:04 +05302615 // Note: Deviated from redfish privilege registry for GET & HEAD
2616 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002617 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302618 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2619 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002620 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2621 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2622 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2623 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002624 }
2625
2626 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002627 void doGet(crow::Response& res, const crow::Request& req,
2628 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002629 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002630 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002631 if (params.size() != 1)
2632 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002633 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002634 return;
2635 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002636 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002637 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2638 }
2639};
2640
2641class CrashdumpFile : public Node
2642{
2643 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002644 CrashdumpFile(CrowApp& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002645 Node(app,
2646 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2647 "<str>/",
2648 std::string(), std::string())
2649 {
AppaRao Puli39460282020-04-07 17:03:04 +05302650 // Note: Deviated from redfish privilege registry for GET & HEAD
2651 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002652 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302653 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2654 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002655 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2656 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2657 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2658 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2659 }
2660
2661 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002662 void doGet(crow::Response& res, const crow::Request& req,
2663 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002664 {
2665 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2666 if (params.size() != 2)
2667 {
2668 messages::internalError(asyncResp->res);
2669 return;
2670 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002671 const std::string& logID = params[0];
2672 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002673
Johnathan Mantey043a0532020-03-10 17:15:28 -07002674 auto getStoredLogCallback =
2675 [asyncResp, logID, fileName](
2676 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002677 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002678 if (ec)
2679 {
2680 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2681 << ec.message();
2682 messages::internalError(asyncResp->res);
2683 return;
2684 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002685
Johnathan Mantey043a0532020-03-10 17:15:28 -07002686 std::string dbusFilename{};
2687 std::string dbusTimestamp{};
2688 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002689
Johnathan Mantey043a0532020-03-10 17:15:28 -07002690 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2691 dbusFilepath);
2692
2693 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2694 dbusFilepath.empty())
2695 {
2696 messages::resourceMissingAtURI(asyncResp->res, fileName);
2697 return;
2698 }
2699
2700 // Verify the file name parameter is correct
2701 if (fileName != dbusFilename)
2702 {
2703 messages::resourceMissingAtURI(asyncResp->res, fileName);
2704 return;
2705 }
2706
2707 if (!std::filesystem::exists(dbusFilepath))
2708 {
2709 messages::resourceMissingAtURI(asyncResp->res, fileName);
2710 return;
2711 }
2712 std::ifstream ifs(dbusFilepath, std::ios::in |
2713 std::ios::binary |
2714 std::ios::ate);
2715 std::ifstream::pos_type fileSize = ifs.tellg();
2716 if (fileSize < 0)
2717 {
2718 messages::generalError(asyncResp->res);
2719 return;
2720 }
2721 ifs.seekg(0, std::ios::beg);
2722
2723 auto crashData = std::make_unique<char[]>(
2724 static_cast<unsigned int>(fileSize));
2725
2726 ifs.read(crashData.get(), static_cast<int>(fileSize));
2727
2728 // The cast to std::string is intentional in order to use the
2729 // assign() that applies move mechanics
2730 asyncResp->res.body().assign(
2731 static_cast<std::string>(crashData.get()));
2732
2733 // Configure this to be a file download when accessed from
2734 // a browser
2735 asyncResp->res.addHeader("Content-Disposition", "attachment");
2736 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002737 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002738 std::move(getStoredLogCallback), crashdumpObject,
2739 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002740 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002741 }
2742};
2743
Jason M. Bills424c4172019-03-21 13:50:33 -07002744class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002745{
2746 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002747 OnDemandCrashdump(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002748 Node(app,
2749 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2750 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002751 {
AppaRao Puli39460282020-04-07 17:03:04 +05302752 // Note: Deviated from redfish privilege registry for GET & HEAD
2753 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002754 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302755 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2756 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2757 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2758 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2759 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2760 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002761 }
2762
2763 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002764 void doPost(crow::Response& res, const crow::Request& req,
2765 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002766 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002767 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002768
James Feistfe306722020-03-12 16:32:08 -07002769 auto generateonDemandLogCallback = [asyncResp,
2770 req](const boost::system::error_code
2771 ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002772 const std::string& resp) {
James Feist46229572020-02-19 15:11:58 -08002773 if (ec)
2774 {
2775 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002776 {
James Feist46229572020-02-19 15:11:58 -08002777 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002778 }
James Feist46229572020-02-19 15:11:58 -08002779 else if (ec.value() ==
2780 boost::system::errc::device_or_resource_busy)
2781 {
2782 messages::serviceTemporarilyUnavailable(asyncResp->res,
2783 "60");
2784 }
2785 else
2786 {
2787 messages::internalError(asyncResp->res);
2788 }
2789 return;
2790 }
2791 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002792 [](boost::system::error_code err, sdbusplus::message::message&,
2793 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002794 if (!err)
2795 {
James Feiste5d50062020-05-11 17:29:00 -07002796 taskData->messages.emplace_back(
2797 messages::taskCompletedOK(
2798 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002799 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002800 }
James Feist32898ce2020-03-10 16:16:52 -07002801 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002802 },
James Feist46229572020-02-19 15:11:58 -08002803 "type='signal',interface='org.freedesktop.DBus.Properties',"
2804 "member='PropertiesChanged',arg0namespace='com.intel."
2805 "crashdump'");
2806 task->startTimer(std::chrono::minutes(5));
2807 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002808 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002809 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002810 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002811 std::move(generateonDemandLogCallback), crashdumpObject,
2812 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002813 }
2814};
2815
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002816class TelemetryCrashdump : public Node
2817{
2818 public:
2819 TelemetryCrashdump(CrowApp& app) :
2820 Node(app,
2821 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2822 "Crashdump.Telemetry/")
2823 {
2824 // Note: Deviated from redfish privilege registry for GET & HEAD
2825 // method for security reasons.
2826 entityPrivileges = {
2827 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2828 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2829 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2830 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2831 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2832 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2833 }
2834
2835 private:
2836 void doPost(crow::Response& res, const crow::Request& req,
2837 const std::vector<std::string>& params) override
2838 {
2839 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2840
2841 auto generateTelemetryLogCallback = [asyncResp, req](
2842 const boost::system::error_code
2843 ec,
2844 const std::string& resp) {
2845 if (ec)
2846 {
2847 if (ec.value() == boost::system::errc::operation_not_supported)
2848 {
2849 messages::resourceInStandby(asyncResp->res);
2850 }
2851 else if (ec.value() ==
2852 boost::system::errc::device_or_resource_busy)
2853 {
2854 messages::serviceTemporarilyUnavailable(asyncResp->res,
2855 "60");
2856 }
2857 else
2858 {
2859 messages::internalError(asyncResp->res);
2860 }
2861 return;
2862 }
2863 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2864 [](boost::system::error_code err, sdbusplus::message::message&,
2865 const std::shared_ptr<task::TaskData>& taskData) {
2866 if (!err)
2867 {
2868 taskData->messages.emplace_back(
2869 messages::taskCompletedOK(
2870 std::to_string(taskData->index)));
2871 taskData->state = "Completed";
2872 }
2873 return task::completed;
2874 },
2875 "type='signal',interface='org.freedesktop.DBus.Properties',"
2876 "member='PropertiesChanged',arg0namespace='com.intel."
2877 "crashdump'");
2878 task->startTimer(std::chrono::minutes(5));
2879 task->populateResp(asyncResp->res);
2880 task->payload.emplace(req);
2881 };
2882 crow::connections::systemBus->async_method_call(
2883 std::move(generateTelemetryLogCallback), crashdumpObject,
2884 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2885 }
2886};
2887
Jason M. Billse1f26342018-07-18 12:12:00 -07002888class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002889{
2890 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002891 SendRawPECI(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002892 Node(app,
2893 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2894 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002895 {
AppaRao Puli39460282020-04-07 17:03:04 +05302896 // Note: Deviated from redfish privilege registry for GET & HEAD
2897 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002898 entityPrivileges = {
2899 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2900 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2901 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2902 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2903 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2904 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2905 }
2906
2907 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002908 void doPost(crow::Response& res, const crow::Request& req,
2909 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002910 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002911 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002912 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002913
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002914 nlohmann::json reqJson =
2915 nlohmann::json::parse(req.body, nullptr, false);
2916 if (reqJson.find("PECICommands") != reqJson.end())
2917 {
2918 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2919 {
2920 return;
2921 }
2922 uint32_t idx = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002923 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002924 {
2925 if (cmd.size() < 3)
2926 {
2927 std::string s("[");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002928 for (auto const& val : cmd)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002929 {
2930 if (val != *cmd.begin())
2931 {
2932 s += ",";
2933 }
2934 s += std::to_string(val);
2935 }
2936 s += "]";
2937 messages::actionParameterValueFormatError(
2938 res, s, "PECICommands[" + std::to_string(idx) + "]",
2939 "SendRawPeci");
2940 return;
2941 }
2942 idx++;
2943 }
2944 }
2945 else
2946 {
2947 /* This interface is deprecated */
2948 uint8_t clientAddress = 0;
2949 uint8_t readLength = 0;
2950 std::vector<uint8_t> peciCommand;
2951 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2952 "ReadLength", readLength, "PECICommand",
2953 peciCommand))
2954 {
2955 return;
2956 }
2957 peciCommands.push_back({clientAddress, 0, readLength});
2958 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2959 peciCommand.end());
2960 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002961 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002962 auto sendRawPECICallback =
2963 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002964 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002965 if (ec)
2966 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002967 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002968 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002969 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002970 return;
2971 }
2972 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2973 {"PECIResponse", resp}};
2974 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002975 // Call the SendRawPECI command with the provided data
2976 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002977 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002978 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002979 }
2980};
2981
Andrew Geisslercb92c032018-08-17 07:56:14 -07002982/**
2983 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2984 */
2985class DBusLogServiceActionsClear : public Node
2986{
2987 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002988 DBusLogServiceActionsClear(CrowApp& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002989 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002990 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002991 {
2992 entityPrivileges = {
2993 {boost::beast::http::verb::get, {{"Login"}}},
2994 {boost::beast::http::verb::head, {{"Login"}}},
2995 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2996 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2997 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2998 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2999 }
3000
3001 private:
3002 /**
3003 * Function handles POST method request.
3004 * The Clear Log actions does not require any parameter.The action deletes
3005 * all entries found in the Entries collection for this Log Service.
3006 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003007 void doPost(crow::Response& res, const crow::Request& req,
3008 const std::vector<std::string>& params) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07003009 {
3010 BMCWEB_LOG_DEBUG << "Do delete all entries.";
3011
3012 auto asyncResp = std::make_shared<AsyncResp>(res);
3013 // Process response from Logging service.
3014 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
3015 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3016 if (ec)
3017 {
3018 // TODO Handle for specific error code
3019 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3020 asyncResp->res.result(
3021 boost::beast::http::status::internal_server_error);
3022 return;
3023 }
3024
3025 asyncResp->res.result(boost::beast::http::status::no_content);
3026 };
3027
3028 // Make call to Logging service to request Clear Log
3029 crow::connections::systemBus->async_method_call(
3030 resp_handler, "xyz.openbmc_project.Logging",
3031 "/xyz/openbmc_project/logging",
3032 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3033 }
3034};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003035
3036/****************************************************
3037 * Redfish PostCode interfaces
3038 * using DBUS interface: getPostCodesTS
3039 ******************************************************/
3040class PostCodesLogService : public Node
3041{
3042 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003043 PostCodesLogService(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003044 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
3045 {
3046 entityPrivileges = {
3047 {boost::beast::http::verb::get, {{"Login"}}},
3048 {boost::beast::http::verb::head, {{"Login"}}},
3049 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3050 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3051 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3052 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3053 }
3054
3055 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003056 void doGet(crow::Response& res, const crow::Request& req,
3057 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003058 {
3059 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3060
3061 asyncResp->res.jsonValue = {
3062 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
3063 {"@odata.type", "#LogService.v1_1_0.LogService"},
3064 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
3065 {"Name", "POST Code Log Service"},
3066 {"Description", "POST Code Log Service"},
3067 {"Id", "BIOS POST Code Log"},
3068 {"OverWritePolicy", "WrapsWhenFull"},
3069 {"Entries",
3070 {{"@odata.id",
3071 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
3072 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3073 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
3074 "Actions/LogService.ClearLog"}};
3075 }
3076};
3077
3078class PostCodesClear : public Node
3079{
3080 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003081 PostCodesClear(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003082 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
3083 "LogService.ClearLog/")
3084 {
3085 entityPrivileges = {
3086 {boost::beast::http::verb::get, {{"Login"}}},
3087 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05303088 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
3089 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
3090 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
3091 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003092 }
3093
3094 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003095 void doPost(crow::Response& res, const crow::Request& req,
3096 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003097 {
3098 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
3099
3100 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3101 // Make call to post-code service to request clear all
3102 crow::connections::systemBus->async_method_call(
3103 [asyncResp](const boost::system::error_code ec) {
3104 if (ec)
3105 {
3106 // TODO Handle for specific error code
3107 BMCWEB_LOG_ERROR
3108 << "doClearPostCodes resp_handler got error " << ec;
3109 asyncResp->res.result(
3110 boost::beast::http::status::internal_server_error);
3111 messages::internalError(asyncResp->res);
3112 return;
3113 }
3114 },
3115 "xyz.openbmc_project.State.Boot.PostCode",
3116 "/xyz/openbmc_project/State/Boot/PostCode",
3117 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3118 }
3119};
3120
3121static void fillPostCodeEntry(
3122 std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003123 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003124 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3125 const uint64_t skip = 0, const uint64_t top = 0)
3126{
3127 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003128 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08003129 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003130
3131 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003132 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003133
3134 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003135 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003136 {
3137 currentCodeIndex++;
3138 std::string postcodeEntryID =
3139 "B" + std::to_string(bootIndex) + "-" +
3140 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3141
3142 uint64_t usecSinceEpoch = code.first;
3143 uint64_t usTimeOffset = 0;
3144
3145 if (1 == currentCodeIndex)
3146 { // already incremented
3147 firstCodeTimeUs = code.first;
3148 }
3149 else
3150 {
3151 usTimeOffset = code.first - firstCodeTimeUs;
3152 }
3153
3154 // skip if no specific codeIndex is specified and currentCodeIndex does
3155 // not fall between top and skip
3156 if ((codeIndex == 0) &&
3157 (currentCodeIndex <= skip || currentCodeIndex > top))
3158 {
3159 continue;
3160 }
3161
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003162 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003163 // currentIndex
3164 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3165 {
3166 // This is done for simplicity. 1st entry is needed to calculate
3167 // time offset. To improve efficiency, one can get to the entry
3168 // directly (possibly with flatmap's nth method)
3169 continue;
3170 }
3171
3172 // currentCodeIndex is within top and skip or equal to specified code
3173 // index
3174
3175 // Get the Created time from the timestamp
3176 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003177 entryTimeStr = crow::utility::getDateTime(
3178 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08003179
3180 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3181 std::ostringstream hexCode;
3182 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
3183 << code.second;
3184 std::ostringstream timeOffsetStr;
3185 // Set Fixed -Point Notation
3186 timeOffsetStr << std::fixed;
3187 // Set precision to 4 digits
3188 timeOffsetStr << std::setprecision(4);
3189 // Add double to stream
3190 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3191 std::vector<std::string> messageArgs = {
3192 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3193
3194 // Get MessageArgs template from message registry
3195 std::string msg;
3196 if (message != nullptr)
3197 {
3198 msg = message->message;
3199
3200 // fill in this post code value
3201 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003202 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003203 {
3204 std::string argStr = "%" + std::to_string(++i);
3205 size_t argPos = msg.find(argStr);
3206 if (argPos != std::string::npos)
3207 {
3208 msg.replace(argPos, argStr.length(), messageArg);
3209 }
3210 }
3211 }
3212
Tim Leed4342a92020-04-27 11:47:58 +08003213 // Get Severity template from message registry
3214 std::string severity;
3215 if (message != nullptr)
3216 {
3217 severity = message->severity;
3218 }
3219
ZhikuiRena3316fc2020-01-29 14:58:08 -08003220 // add to AsyncResp
3221 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003222 nlohmann::json& bmcLogEntry = logEntryArray.back();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003223 bmcLogEntry = {
3224 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
3225 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
3226 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
3227 "PostCodes/Entries/" +
3228 postcodeEntryID},
3229 {"Name", "POST Code Log Entry"},
3230 {"Id", postcodeEntryID},
3231 {"Message", std::move(msg)},
3232 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
3233 {"MessageArgs", std::move(messageArgs)},
3234 {"EntryType", "Event"},
3235 {"Severity", std::move(severity)},
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05003236 {"Created", entryTimeStr}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08003237 }
3238}
3239
3240static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
3241 const uint16_t bootIndex,
3242 const uint64_t codeIndex)
3243{
3244 crow::connections::systemBus->async_method_call(
3245 [aResp, bootIndex, codeIndex](
3246 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003247 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003248 if (ec)
3249 {
3250 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3251 messages::internalError(aResp->res);
3252 return;
3253 }
3254
3255 // skip the empty postcode boots
3256 if (postcode.empty())
3257 {
3258 return;
3259 }
3260
3261 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3262
3263 aResp->res.jsonValue["Members@odata.count"] =
3264 aResp->res.jsonValue["Members"].size();
3265 },
3266 "xyz.openbmc_project.State.Boot.PostCode",
3267 "/xyz/openbmc_project/State/Boot/PostCode",
3268 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3269 bootIndex);
3270}
3271
3272static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
3273 const uint16_t bootIndex,
3274 const uint16_t bootCount,
3275 const uint64_t entryCount, const uint64_t skip,
3276 const uint64_t top)
3277{
3278 crow::connections::systemBus->async_method_call(
3279 [aResp, bootIndex, bootCount, entryCount, skip,
3280 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003281 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003282 if (ec)
3283 {
3284 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3285 messages::internalError(aResp->res);
3286 return;
3287 }
3288
3289 uint64_t endCount = entryCount;
3290 if (!postcode.empty())
3291 {
3292 endCount = entryCount + postcode.size();
3293
3294 if ((skip < endCount) && ((top + skip) > entryCount))
3295 {
3296 uint64_t thisBootSkip =
3297 std::max(skip, entryCount) - entryCount;
3298 uint64_t thisBootTop =
3299 std::min(top + skip, endCount) - entryCount;
3300
3301 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3302 thisBootSkip, thisBootTop);
3303 }
3304 aResp->res.jsonValue["Members@odata.count"] = endCount;
3305 }
3306
3307 // continue to previous bootIndex
3308 if (bootIndex < bootCount)
3309 {
3310 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3311 bootCount, endCount, skip, top);
3312 }
3313 else
3314 {
3315 aResp->res.jsonValue["Members@odata.nextLink"] =
3316 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3317 "Entries?$skip=" +
3318 std::to_string(skip + top);
3319 }
3320 },
3321 "xyz.openbmc_project.State.Boot.PostCode",
3322 "/xyz/openbmc_project/State/Boot/PostCode",
3323 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3324 bootIndex);
3325}
3326
3327static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
3328 const uint64_t skip, const uint64_t top)
3329{
3330 uint64_t entryCount = 0;
3331 crow::connections::systemBus->async_method_call(
3332 [aResp, entryCount, skip,
3333 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003334 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003335 if (ec)
3336 {
3337 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3338 messages::internalError(aResp->res);
3339 return;
3340 }
3341 auto pVal = std::get_if<uint16_t>(&bootCount);
3342 if (pVal)
3343 {
3344 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3345 }
3346 else
3347 {
3348 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3349 }
3350 },
3351 "xyz.openbmc_project.State.Boot.PostCode",
3352 "/xyz/openbmc_project/State/Boot/PostCode",
3353 "org.freedesktop.DBus.Properties", "Get",
3354 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3355}
3356
3357class PostCodesEntryCollection : public Node
3358{
3359 public:
3360 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003361 PostCodesEntryCollection(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003362 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
3363 {
3364 entityPrivileges = {
3365 {boost::beast::http::verb::get, {{"Login"}}},
3366 {boost::beast::http::verb::head, {{"Login"}}},
3367 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3368 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3369 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3370 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3371 }
3372
3373 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003374 void doGet(crow::Response& res, const crow::Request& req,
3375 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003376 {
3377 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3378
3379 asyncResp->res.jsonValue["@odata.type"] =
3380 "#LogEntryCollection.LogEntryCollection";
3381 asyncResp->res.jsonValue["@odata.context"] =
3382 "/redfish/v1/"
3383 "$metadata#LogEntryCollection.LogEntryCollection";
3384 asyncResp->res.jsonValue["@odata.id"] =
3385 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3386 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3387 asyncResp->res.jsonValue["Description"] =
3388 "Collection of POST Code Log Entries";
3389 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3390 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3391
3392 uint64_t skip = 0;
3393 uint64_t top = maxEntriesPerPage; // Show max entries by default
3394 if (!getSkipParam(asyncResp->res, req, skip))
3395 {
3396 return;
3397 }
3398 if (!getTopParam(asyncResp->res, req, top))
3399 {
3400 return;
3401 }
3402 getCurrentBootNumber(asyncResp, skip, top);
3403 }
3404};
3405
3406class PostCodesEntry : public Node
3407{
3408 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003409 PostCodesEntry(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08003410 Node(app,
3411 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
3412 std::string())
3413 {
3414 entityPrivileges = {
3415 {boost::beast::http::verb::get, {{"Login"}}},
3416 {boost::beast::http::verb::head, {{"Login"}}},
3417 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
3418 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
3419 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
3420 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
3421 }
3422
3423 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003424 void doGet(crow::Response& res, const crow::Request& req,
3425 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08003426 {
3427 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3428 if (params.size() != 1)
3429 {
3430 messages::internalError(asyncResp->res);
3431 return;
3432 }
3433
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003434 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003435
3436 size_t bootPos = targetID.find('B');
3437 if (bootPos == std::string::npos)
3438 {
3439 // Requested ID was not found
3440 messages::resourceMissingAtURI(asyncResp->res, targetID);
3441 return;
3442 }
3443 std::string_view bootIndexStr(targetID);
3444 bootIndexStr.remove_prefix(bootPos + 1);
3445 uint16_t bootIndex = 0;
3446 uint64_t codeIndex = 0;
3447 size_t dashPos = bootIndexStr.find('-');
3448
3449 if (dashPos == std::string::npos)
3450 {
3451 return;
3452 }
3453 std::string_view codeIndexStr(bootIndexStr);
3454 bootIndexStr.remove_suffix(dashPos);
3455 codeIndexStr.remove_prefix(dashPos + 1);
3456
3457 bootIndex = static_cast<uint16_t>(
3458 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
3459 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
3460 if (bootIndex == 0 || codeIndex == 0)
3461 {
3462 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3463 << params[0];
3464 }
3465
3466 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3467 asyncResp->res.jsonValue["@odata.context"] =
3468 "/redfish/v1/$metadata#LogEntry.LogEntry";
3469 asyncResp->res.jsonValue["@odata.id"] =
3470 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3471 "Entries";
3472 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3473 asyncResp->res.jsonValue["Description"] =
3474 "Collection of POST Code Log Entries";
3475 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3476 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3477
3478 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3479 }
3480};
3481
Ed Tanous1da66f72018-07-27 16:13:37 -07003482} // namespace redfish