blob: 9aff5c6e5235fdceaea6de3affa6a0a89c6cc08c [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
George Liuaf61db12021-03-08 19:36:32 +080018#include "http_utility.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>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060025#include <unistd.h>
Jason M. Billse1f26342018-07-18 12:12:00 -070026
John Edward Broadbent7e860f12021-04-08 15:57:16 -070027#include <app.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060028#include <boost/algorithm/string/replace.hpp>
Jason M. Bills4851d452019-03-28 11:27:48 -070029#include <boost/algorithm/string/split.hpp>
30#include <boost/beast/core/span.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060031#include <boost/beast/http.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070032#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080033#include <boost/system/linux_error.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070034#include <error_messages.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050035
George Liuaf61db12021-03-08 19:36:32 +080036#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070037#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080038#include <optional>
Jason M. Billscd225da2019-05-08 15:31:57 -070039#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080040#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070041
42namespace redfish
43{
44
Gunnar Mills1214b7e2020-06-04 10:11:30 -050045constexpr char const* crashdumpObject = "com.intel.crashdump";
46constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050047constexpr char const* crashdumpInterface = "com.intel.crashdump";
48constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070049 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050050constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070051 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070052constexpr char const* crashdumpTelemetryInterface =
53 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070054
Jason M. Bills4851d452019-03-28 11:27:48 -070055namespace message_registries
56{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050057static const Message* getMessageFromRegistry(
58 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070059 const boost::beast::span<const MessageEntry> registry)
60{
61 boost::beast::span<const MessageEntry>::const_iterator messageIt =
62 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050063 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070064 return !std::strcmp(messageEntry.first,
65 messageKey.c_str());
66 });
67 if (messageIt != registry.cend())
68 {
69 return &messageIt->second;
70 }
71
72 return nullptr;
73}
74
Gunnar Mills1214b7e2020-06-04 10:11:30 -050075static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070076{
77 // Redfish MessageIds are in the form
78 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
79 // the right Message
80 std::vector<std::string> fields;
81 fields.reserve(4);
82 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050083 std::string& registryName = fields[0];
84 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070085
86 // Find the right registry and check it for the MessageKey
87 if (std::string(base::header.registryPrefix) == registryName)
88 {
89 return getMessageFromRegistry(
90 messageKey, boost::beast::span<const MessageEntry>(base::registry));
91 }
92 if (std::string(openbmc::header.registryPrefix) == registryName)
93 {
94 return getMessageFromRegistry(
95 messageKey,
96 boost::beast::span<const MessageEntry>(openbmc::registry));
97 }
98 return nullptr;
99}
100} // namespace message_registries
101
James Feistf6150402019-01-08 10:36:20 -0800102namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700103
Andrew Geisslercb92c032018-08-17 07:56:14 -0700104using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500105 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
106 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700107
108using GetManagedObjectsType = boost::container::flat_map<
109 sdbusplus::message::object_path,
110 boost::container::flat_map<std::string, GetManagedPropertyType>>;
111
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500112inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700113{
Ed Tanousd4d25792020-09-29 15:15:03 -0700114 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
115 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
116 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
117 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700118 {
119 return "Critical";
120 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700121 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
122 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
123 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700124 {
125 return "OK";
126 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700127 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700128 {
129 return "Warning";
130 }
131 return "";
132}
133
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700134inline static int getJournalMetadata(sd_journal* journal,
135 const std::string_view& field,
136 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700137{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500138 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700139 size_t length = 0;
140 int ret = 0;
141 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700142 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500143 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700144 if (ret < 0)
145 {
146 return ret;
147 }
Ed Tanous39e77502019-03-04 17:35:53 -0800148 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700149 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000150 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700151 return ret;
152}
153
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700154inline static int getJournalMetadata(sd_journal* journal,
155 const std::string_view& field,
156 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700157{
158 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800159 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700160 // Get the metadata from the requested field of the journal entry
161 ret = getJournalMetadata(journal, field, metadata);
162 if (ret < 0)
163 {
164 return ret;
165 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000166 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700167 return ret;
168}
169
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700170inline static bool getEntryTimestamp(sd_journal* journal,
171 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800172{
173 int ret = 0;
174 uint64_t timestamp = 0;
175 ret = sd_journal_get_realtime_usec(journal, &timestamp);
176 if (ret < 0)
177 {
178 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
179 << strerror(-ret);
180 return false;
181 }
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500182 entryTimestamp = crow::utility::getDateTime(
183 static_cast<std::time_t>(timestamp / 1000 / 1000));
184 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800185}
186
zhanghch058d1b46d2021-04-01 11:18:24 +0800187static bool getSkipParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
188 const crow::Request& req, uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700189{
James Feist5a7e8772020-07-22 09:08:38 -0700190 boost::urls::url_view::params_type::iterator it =
191 req.urlParams.find("$skip");
192 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700193 {
James Feist5a7e8772020-07-22 09:08:38 -0700194 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500195 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700196 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
197 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700198 {
199
zhanghch058d1b46d2021-04-01 11:18:24 +0800200 messages::queryParameterValueTypeError(
201 asyncResp->res, std::string(skipParam), "$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700202 return false;
203 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700204 }
205 return true;
206}
207
Ed Tanous271584a2019-07-09 16:24:22 -0700208static constexpr const uint64_t maxEntriesPerPage = 1000;
zhanghch058d1b46d2021-04-01 11:18:24 +0800209static bool getTopParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
210 const crow::Request& req, uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700211{
James Feist5a7e8772020-07-22 09:08:38 -0700212 boost::urls::url_view::params_type::iterator it =
213 req.urlParams.find("$top");
214 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700215 {
James Feist5a7e8772020-07-22 09:08:38 -0700216 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500217 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700218 top = std::strtoul(topParam.c_str(), &ptr, 10);
219 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700220 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800221 messages::queryParameterValueTypeError(
222 asyncResp->res, std::string(topParam), "$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700223 return false;
224 }
Ed Tanous271584a2019-07-09 16:24:22 -0700225 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700226 {
227
228 messages::queryParameterOutOfRange(
zhanghch058d1b46d2021-04-01 11:18:24 +0800229 asyncResp->res, std::to_string(top), "$top",
Jason M. Bills16428a12018-11-02 12:42:29 -0700230 "1-" + std::to_string(maxEntriesPerPage));
231 return false;
232 }
233 }
234 return true;
235}
236
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700237inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
238 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700239{
240 int ret = 0;
241 static uint64_t prevTs = 0;
242 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700243 if (firstEntry)
244 {
245 prevTs = 0;
246 }
247
Jason M. Bills16428a12018-11-02 12:42:29 -0700248 // Get the entry timestamp
249 uint64_t curTs = 0;
250 ret = sd_journal_get_realtime_usec(journal, &curTs);
251 if (ret < 0)
252 {
253 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
254 << strerror(-ret);
255 return false;
256 }
257 // If the timestamp isn't unique, increment the index
258 if (curTs == prevTs)
259 {
260 index++;
261 }
262 else
263 {
264 // Otherwise, reset it
265 index = 0;
266 }
267 // Save the timestamp
268 prevTs = curTs;
269
270 entryID = std::to_string(curTs);
271 if (index > 0)
272 {
273 entryID += "_" + std::to_string(index);
274 }
275 return true;
276}
277
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500278static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700279 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700280{
Ed Tanous271584a2019-07-09 16:24:22 -0700281 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700282 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700283 if (firstEntry)
284 {
285 prevTs = 0;
286 }
287
Jason M. Bills95820182019-04-22 16:25:34 -0700288 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700289 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700290 std::tm timeStruct = {};
291 std::istringstream entryStream(logEntry);
292 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
293 {
294 curTs = std::mktime(&timeStruct);
295 }
296 // If the timestamp isn't unique, increment the index
297 if (curTs == prevTs)
298 {
299 index++;
300 }
301 else
302 {
303 // Otherwise, reset it
304 index = 0;
305 }
306 // Save the timestamp
307 prevTs = curTs;
308
309 entryID = std::to_string(curTs);
310 if (index > 0)
311 {
312 entryID += "_" + std::to_string(index);
313 }
314 return true;
315}
316
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700317inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800318 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
319 const std::string& entryID, uint64_t& timestamp,
320 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700321{
322 if (entryID.empty())
323 {
324 return false;
325 }
326 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800327 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700328
Ed Tanous81ce6092020-12-17 16:54:55 +0000329 auto underscorePos = tsStr.find('_');
Jason M. Bills16428a12018-11-02 12:42:29 -0700330 if (underscorePos != tsStr.npos)
331 {
332 // Timestamp has an index
333 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800334 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700335 indexStr.remove_prefix(underscorePos + 1);
336 std::size_t pos;
337 try
338 {
Ed Tanous39e77502019-03-04 17:35:53 -0800339 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500341 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700342 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800343 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700344 return false;
345 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500346 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700347 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800348 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700349 return false;
350 }
351 if (pos != indexStr.size())
352 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800353 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700354 return false;
355 }
356 }
357 // Timestamp has no index
358 std::size_t pos;
359 try
360 {
Ed Tanous39e77502019-03-04 17:35:53 -0800361 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700362 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500363 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700364 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800365 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700366 return false;
367 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500368 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700369 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800370 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700371 return false;
372 }
373 if (pos != tsStr.size())
374 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800375 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700376 return false;
377 }
378 return true;
379}
380
Jason M. Bills95820182019-04-22 16:25:34 -0700381static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500382 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700383{
384 static const std::filesystem::path redfishLogDir = "/var/log";
385 static const std::string redfishLogFilename = "redfish";
386
387 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500388 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700389 std::filesystem::directory_iterator(redfishLogDir))
390 {
391 // If we find a redfish log file, save the path
392 std::string filename = dirEnt.path().filename();
393 if (boost::starts_with(filename, redfishLogFilename))
394 {
395 redfishLogFiles.emplace_back(redfishLogDir / filename);
396 }
397 }
398 // As the log files rotate, they are appended with a ".#" that is higher for
399 // the older logs. Since we don't expect more than 10 log files, we
400 // can just sort the list to get them in order from newest to oldest
401 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
402
403 return !redfishLogFiles.empty();
404}
405
zhanghch058d1b46d2021-04-01 11:18:24 +0800406inline void
407 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
408 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500409{
410 std::string dumpPath;
411 if (dumpType == "BMC")
412 {
413 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
414 }
415 else if (dumpType == "System")
416 {
417 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
418 }
419 else
420 {
421 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
422 messages::internalError(asyncResp->res);
423 return;
424 }
425
426 crow::connections::systemBus->async_method_call(
427 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
428 GetManagedObjectsType& resp) {
429 if (ec)
430 {
431 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
432 messages::internalError(asyncResp->res);
433 return;
434 }
435
436 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
437 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500438 std::string dumpEntryPath =
439 "/xyz/openbmc_project/dump/" +
440 std::string(boost::algorithm::to_lower_copy(dumpType)) +
441 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500442
443 for (auto& object : resp)
444 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500445 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500446 {
447 continue;
448 }
449 std::time_t timestamp;
450 uint64_t size = 0;
451 entriesArray.push_back({});
452 nlohmann::json& thisEntry = entriesArray.back();
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000453
454 std::string entryID = object.first.filename();
455 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500456 {
457 continue;
458 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500459
460 for (auto& interfaceMap : object.second)
461 {
462 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
463 {
464
465 for (auto& propertyMap : interfaceMap.second)
466 {
467 if (propertyMap.first == "Size")
468 {
469 auto sizePtr =
470 std::get_if<uint64_t>(&propertyMap.second);
471 if (sizePtr == nullptr)
472 {
473 messages::internalError(asyncResp->res);
474 break;
475 }
476 size = *sizePtr;
477 break;
478 }
479 }
480 }
481 else if (interfaceMap.first ==
482 "xyz.openbmc_project.Time.EpochTime")
483 {
484
485 for (auto& propertyMap : interfaceMap.second)
486 {
487 if (propertyMap.first == "Elapsed")
488 {
489 const uint64_t* usecsTimeStamp =
490 std::get_if<uint64_t>(&propertyMap.second);
491 if (usecsTimeStamp == nullptr)
492 {
493 messages::internalError(asyncResp->res);
494 break;
495 }
496 timestamp =
497 static_cast<std::time_t>(*usecsTimeStamp);
498 break;
499 }
500 }
501 }
502 }
503
George Liu0ef217f2021-03-08 14:24:46 +0800504 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500505 thisEntry["@odata.id"] = dumpPath + entryID;
506 thisEntry["Id"] = entryID;
507 thisEntry["EntryType"] = "Event";
508 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
509 thisEntry["Name"] = dumpType + " Dump Entry";
510
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500511 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500512
513 if (dumpType == "BMC")
514 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500515 thisEntry["DiagnosticDataType"] = "Manager";
516 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500517 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
518 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500519 }
520 else if (dumpType == "System")
521 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500522 thisEntry["DiagnosticDataType"] = "OEM";
523 thisEntry["OEMDiagnosticDataType"] = "System";
524 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500525 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
526 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500527 }
528 }
529 asyncResp->res.jsonValue["Members@odata.count"] =
530 entriesArray.size();
531 },
532 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
533 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
534}
535
zhanghch058d1b46d2021-04-01 11:18:24 +0800536inline void
537 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
538 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500539{
540 std::string dumpPath;
541 if (dumpType == "BMC")
542 {
543 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
544 }
545 else if (dumpType == "System")
546 {
547 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
548 }
549 else
550 {
551 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
552 messages::internalError(asyncResp->res);
553 return;
554 }
555
556 crow::connections::systemBus->async_method_call(
557 [asyncResp, entryID, dumpPath, dumpType](
558 const boost::system::error_code ec, GetManagedObjectsType& resp) {
559 if (ec)
560 {
561 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
562 messages::internalError(asyncResp->res);
563 return;
564 }
565
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500566 bool foundDumpEntry = false;
567 std::string dumpEntryPath =
568 "/xyz/openbmc_project/dump/" +
569 std::string(boost::algorithm::to_lower_copy(dumpType)) +
570 "/entry/";
571
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500572 for (auto& objectPath : resp)
573 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500574 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500575 {
576 continue;
577 }
578
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500579 foundDumpEntry = true;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500580 std::time_t timestamp;
581 uint64_t size = 0;
582
583 for (auto& interfaceMap : objectPath.second)
584 {
585 if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
586 {
587 for (auto& propertyMap : interfaceMap.second)
588 {
589 if (propertyMap.first == "Size")
590 {
591 auto sizePtr =
592 std::get_if<uint64_t>(&propertyMap.second);
593 if (sizePtr == nullptr)
594 {
595 messages::internalError(asyncResp->res);
596 break;
597 }
598 size = *sizePtr;
599 break;
600 }
601 }
602 }
603 else if (interfaceMap.first ==
604 "xyz.openbmc_project.Time.EpochTime")
605 {
606 for (auto& propertyMap : interfaceMap.second)
607 {
608 if (propertyMap.first == "Elapsed")
609 {
610 const uint64_t* usecsTimeStamp =
611 std::get_if<uint64_t>(&propertyMap.second);
612 if (usecsTimeStamp == nullptr)
613 {
614 messages::internalError(asyncResp->res);
615 break;
616 }
617 timestamp =
618 static_cast<std::time_t>(*usecsTimeStamp);
619 break;
620 }
621 }
622 }
623 }
624
625 asyncResp->res.jsonValue["@odata.type"] =
George Liu0ef217f2021-03-08 14:24:46 +0800626 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500627 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
628 asyncResp->res.jsonValue["Id"] = entryID;
629 asyncResp->res.jsonValue["EntryType"] = "Event";
630 asyncResp->res.jsonValue["Created"] =
631 crow::utility::getDateTime(timestamp);
632 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
633
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500634 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500635
636 if (dumpType == "BMC")
637 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500638 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
639 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500640 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
641 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500642 }
643 else if (dumpType == "System")
644 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500645 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
646 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500647 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500648 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500649 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
650 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500651 }
652 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500653 if (foundDumpEntry == false)
654 {
655 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
656 messages::internalError(asyncResp->res);
657 return;
658 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500659 },
660 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
661 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
662}
663
zhanghch058d1b46d2021-04-01 11:18:24 +0800664inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800665 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500666 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500667{
George Liu3de8d8b2021-03-22 17:49:39 +0800668 auto respHandler = [asyncResp,
669 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500670 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
671 if (ec)
672 {
George Liu3de8d8b2021-03-22 17:49:39 +0800673 if (ec.value() == EBADR)
674 {
675 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
676 return;
677 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500678 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
679 << ec;
680 messages::internalError(asyncResp->res);
681 return;
682 }
683 };
684 crow::connections::systemBus->async_method_call(
685 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500686 "/xyz/openbmc_project/dump/" +
687 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
688 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500689 "xyz.openbmc_project.Object.Delete", "Delete");
690}
691
zhanghch058d1b46d2021-04-01 11:18:24 +0800692inline void
693 createDumpTaskCallback(const crow::Request& req,
694 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
695 const uint32_t& dumpId, const std::string& dumpPath,
696 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500697{
698 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500699 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500700 boost::system::error_code err, sdbusplus::message::message& m,
701 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000702 if (err)
703 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500704 BMCWEB_LOG_ERROR << "Error in creating a dump";
705 taskData->state = "Cancelled";
706 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000707 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500708 std::vector<std::pair<
709 std::string,
710 std::vector<std::pair<std::string, std::variant<std::string>>>>>
711 interfacesList;
712
713 sdbusplus::message::object_path objPath;
714
715 m.read(objPath, interfacesList);
716
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500717 if (objPath.str ==
718 "/xyz/openbmc_project/dump/" +
719 std::string(boost::algorithm::to_lower_copy(dumpType)) +
720 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500721 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500722 nlohmann::json retMessage = messages::success();
723 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500724
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500725 std::string headerLoc =
726 "Location: " + dumpPath + std::to_string(dumpId);
727 taskData->payload->httpHeaders.emplace_back(
728 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500729
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500730 taskData->state = "Completed";
731 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500732 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500733 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500734 },
735 "type='signal',interface='org.freedesktop.DBus."
736 "ObjectManager',"
737 "member='InterfacesAdded', "
738 "path='/xyz/openbmc_project/dump'");
739
740 task->startTimer(std::chrono::minutes(3));
741 task->populateResp(asyncResp->res);
742 task->payload.emplace(req);
743}
744
zhanghch058d1b46d2021-04-01 11:18:24 +0800745inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
746 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500747{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500748
749 std::string dumpPath;
750 if (dumpType == "BMC")
751 {
752 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
753 }
754 else if (dumpType == "System")
755 {
756 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
757 }
758 else
759 {
760 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
761 messages::internalError(asyncResp->res);
762 return;
763 }
764
765 std::optional<std::string> diagnosticDataType;
766 std::optional<std::string> oemDiagnosticDataType;
767
768 if (!redfish::json_util::readJson(
769 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
770 "OEMDiagnosticDataType", oemDiagnosticDataType))
771 {
772 return;
773 }
774
775 if (dumpType == "System")
776 {
777 if (!oemDiagnosticDataType || !diagnosticDataType)
778 {
779 BMCWEB_LOG_ERROR << "CreateDump action parameter "
780 "'DiagnosticDataType'/"
781 "'OEMDiagnosticDataType' value not found!";
782 messages::actionParameterMissing(
783 asyncResp->res, "CollectDiagnosticData",
784 "DiagnosticDataType & OEMDiagnosticDataType");
785 return;
786 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700787 if ((*oemDiagnosticDataType != "System") ||
788 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500789 {
790 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
791 messages::invalidObject(asyncResp->res,
792 "System Dump creation parameters");
793 return;
794 }
795 }
796 else if (dumpType == "BMC")
797 {
798 if (!diagnosticDataType)
799 {
800 BMCWEB_LOG_ERROR << "CreateDump action parameter "
801 "'DiagnosticDataType' not found!";
802 messages::actionParameterMissing(
803 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
804 return;
805 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700806 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500807 {
808 BMCWEB_LOG_ERROR
809 << "Wrong parameter value passed for 'DiagnosticDataType'";
810 messages::invalidObject(asyncResp->res,
811 "BMC Dump creation parameters");
812 return;
813 }
814 }
815
816 crow::connections::systemBus->async_method_call(
817 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
818 const uint32_t& dumpId) {
819 if (ec)
820 {
821 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
822 messages::internalError(asyncResp->res);
823 return;
824 }
825 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
826
827 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
828 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500829 "xyz.openbmc_project.Dump.Manager",
830 "/xyz/openbmc_project/dump/" +
831 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500832 "xyz.openbmc_project.Dump.Create", "CreateDump");
833}
834
zhanghch058d1b46d2021-04-01 11:18:24 +0800835inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
836 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500837{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500838 std::string dumpTypeLowerCopy =
839 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800840
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500841 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500842 [asyncResp, dumpType](const boost::system::error_code ec,
843 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500844 if (ec)
845 {
846 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
847 messages::internalError(asyncResp->res);
848 return;
849 }
850
851 for (const std::string& path : subTreePaths)
852 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000853 sdbusplus::message::object_path objPath(path);
854 std::string logID = objPath.filename();
855 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500856 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000857 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500858 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000859 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500860 }
861 },
862 "xyz.openbmc_project.ObjectMapper",
863 "/xyz/openbmc_project/object_mapper",
864 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500865 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
866 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
867 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500868}
869
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700870inline static void parseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500871 const std::vector<std::pair<std::string, VariantType>>& params,
872 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700873{
874 for (auto property : params)
875 {
876 if (property.first == "Timestamp")
877 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500878 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500879 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700880 if (value != nullptr)
881 {
882 timestamp = *value;
883 }
884 }
885 else if (property.first == "Filename")
886 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500887 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500888 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700889 if (value != nullptr)
890 {
891 filename = *value;
892 }
893 }
894 else if (property.first == "Log")
895 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500896 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500897 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700898 if (value != nullptr)
899 {
900 logfile = *value;
901 }
902 }
903 }
904}
905
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500906constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700907inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700908{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800909 /**
910 * Functions triggers appropriate requests on DBus
911 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700912 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanous432a8902021-06-14 15:28:56 -0700913 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700914 .methods(boost::beast::http::verb::get)(
915 [](const crow::Request&,
916 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
917
918 {
919 // Collections don't include the static data added by SubRoute
920 // because it has a duplicate entry for members
921 asyncResp->res.jsonValue["@odata.type"] =
922 "#LogServiceCollection.LogServiceCollection";
923 asyncResp->res.jsonValue["@odata.id"] =
924 "/redfish/v1/Systems/system/LogServices";
925 asyncResp->res.jsonValue["Name"] =
926 "System Log Services Collection";
927 asyncResp->res.jsonValue["Description"] =
928 "Collection of LogServices for this Computer System";
929 nlohmann::json& logServiceArray =
930 asyncResp->res.jsonValue["Members"];
931 logServiceArray = nlohmann::json::array();
932 logServiceArray.push_back(
933 {{"@odata.id",
934 "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500935#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700936 logServiceArray.push_back(
937 {{"@odata.id",
938 "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600939#endif
940
Jason M. Billsd53dd412019-02-12 17:16:22 -0800941#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700942 logServiceArray.push_back(
943 {{"@odata.id",
944 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800945#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700946 asyncResp->res.jsonValue["Members@odata.count"] =
947 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800948
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700949 crow::connections::systemBus->async_method_call(
950 [asyncResp](const boost::system::error_code ec,
951 const std::vector<std::string>& subtreePath) {
952 if (ec)
953 {
954 BMCWEB_LOG_ERROR << ec;
955 return;
956 }
ZhikuiRena3316fc2020-01-29 14:58:08 -0800957
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700958 for (auto& pathStr : subtreePath)
959 {
960 if (pathStr.find("PostCode") != std::string::npos)
961 {
962 nlohmann::json& logServiceArrayLocal =
963 asyncResp->res.jsonValue["Members"];
964 logServiceArrayLocal.push_back(
965 {{"@odata.id", "/redfish/v1/Systems/system/"
966 "LogServices/PostCodes"}});
967 asyncResp->res
968 .jsonValue["Members@odata.count"] =
969 logServiceArrayLocal.size();
970 return;
971 }
972 }
973 },
974 "xyz.openbmc_project.ObjectMapper",
975 "/xyz/openbmc_project/object_mapper",
976 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
977 0, std::array<const char*, 1>{postCodeIface});
978 });
979}
980
981inline void requestRoutesEventLogService(App& app)
982{
983 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanous432a8902021-06-14 15:28:56 -0700984 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700985 .methods(
986 boost::beast::http::verb::
987 get)([](const crow::Request&,
988 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
989 asyncResp->res.jsonValue["@odata.id"] =
990 "/redfish/v1/Systems/system/LogServices/EventLog";
991 asyncResp->res.jsonValue["@odata.type"] =
992 "#LogService.v1_1_0.LogService";
993 asyncResp->res.jsonValue["Name"] = "Event Log Service";
994 asyncResp->res.jsonValue["Description"] =
995 "System Event Log Service";
996 asyncResp->res.jsonValue["Id"] = "EventLog";
997 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530998
999 std::pair<std::string, std::string> redfishDateTimeOffset =
1000 crow::utility::getDateTimeOffsetNow();
1001
1002 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1003 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1004 redfishDateTimeOffset.second;
1005
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001006 asyncResp->res.jsonValue["Entries"] = {
1007 {"@odata.id",
1008 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1009 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1010
1011 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1012 "Actions/LogService.ClearLog"}};
1013 });
1014}
1015
1016inline void requestRoutesJournalEventLogClear(App& app)
1017{
1018 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1019 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001020 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001021 .methods(boost::beast::http::verb::post)(
1022 [](const crow::Request&,
1023 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1024 // Clear the EventLog by deleting the log files
1025 std::vector<std::filesystem::path> redfishLogFiles;
1026 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001027 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001028 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001029 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001030 std::error_code ec;
1031 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001032 }
1033 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001034
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001035 // Reload rsyslog so it knows to start new log files
1036 crow::connections::systemBus->async_method_call(
1037 [asyncResp](const boost::system::error_code ec) {
1038 if (ec)
1039 {
1040 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1041 << ec;
1042 messages::internalError(asyncResp->res);
1043 return;
1044 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001045
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001046 messages::success(asyncResp->res);
1047 },
1048 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1049 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1050 "rsyslog.service", "replace");
1051 });
1052}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001053
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001054static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001055 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001056 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001057{
Jason M. Bills95820182019-04-22 16:25:34 -07001058 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001059 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001060 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001061 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001062 {
1063 return 1;
1064 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001065 std::string timestamp = logEntry.substr(0, space);
1066 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001067 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001068 if (entryStart == std::string::npos)
1069 {
1070 return 1;
1071 }
1072 std::string_view entry(logEntry);
1073 entry.remove_prefix(entryStart);
1074 // Use split to separate the entry into its fields
1075 std::vector<std::string> logEntryFields;
1076 boost::split(logEntryFields, entry, boost::is_any_of(","),
1077 boost::token_compress_on);
1078 // We need at least a MessageId to be valid
1079 if (logEntryFields.size() < 1)
1080 {
1081 return 1;
1082 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001083 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001084
Jason M. Bills4851d452019-03-28 11:27:48 -07001085 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001086 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001087 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001088
Jason M. Bills4851d452019-03-28 11:27:48 -07001089 std::string msg;
1090 std::string severity;
1091 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001092 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001093 msg = message->message;
1094 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001095 }
1096
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001097 // Get the MessageArgs from the log if there are any
1098 boost::beast::span<std::string> messageArgs;
1099 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001100 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001101 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001102 // If the first string is empty, assume there are no MessageArgs
1103 std::size_t messageArgsSize = 0;
1104 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001105 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001106 messageArgsSize = logEntryFields.size() - 1;
1107 }
1108
Ed Tanous23a21a12020-07-25 04:45:05 +00001109 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001110
1111 // Fill the MessageArgs into the Message
1112 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001113 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001114 {
1115 std::string argStr = "%" + std::to_string(++i);
1116 size_t argPos = msg.find(argStr);
1117 if (argPos != std::string::npos)
1118 {
1119 msg.replace(argPos, argStr.length(), messageArg);
1120 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001121 }
1122 }
1123
Jason M. Bills95820182019-04-22 16:25:34 -07001124 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1125 // format which matches the Redfish format except for the fractional seconds
1126 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001127 std::size_t dot = timestamp.find_first_of('.');
1128 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001129 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001130 {
Jason M. Bills95820182019-04-22 16:25:34 -07001131 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001132 }
1133
1134 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001135 logEntryJson = {
George Liu0ef217f2021-03-08 14:24:46 +08001136 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001137 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001138 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001139 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001140 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001141 {"Id", logEntryID},
1142 {"Message", std::move(msg)},
1143 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001144 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001145 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001146 {"Severity", std::move(severity)},
1147 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001148 return 0;
1149}
1150
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001151inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001152{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001153 BMCWEB_ROUTE(app,
1154 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07001155 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001156 .methods(boost::beast::http::verb::get)(
1157 [](const crow::Request& req,
1158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1159 uint64_t skip = 0;
1160 uint64_t top = maxEntriesPerPage; // Show max entries by default
1161 if (!getSkipParam(asyncResp, req, skip))
Jason M. Bills95820182019-04-22 16:25:34 -07001162 {
Jason M. Bills95820182019-04-22 16:25:34 -07001163 return;
1164 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001165 if (!getTopParam(asyncResp, req, top))
Jason M. Bills897967d2019-07-29 17:05:30 -07001166 {
Jason M. Bills897967d2019-07-29 17:05:30 -07001167 return;
1168 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001169 // Collections don't include the static data added by SubRoute
1170 // because it has a duplicate entry for members
1171 asyncResp->res.jsonValue["@odata.type"] =
1172 "#LogEntryCollection.LogEntryCollection";
1173 asyncResp->res.jsonValue["@odata.id"] =
1174 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1175 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1176 asyncResp->res.jsonValue["Description"] =
1177 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001178
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001179 nlohmann::json& logEntryArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001180 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001181 logEntryArray = nlohmann::json::array();
1182 // Go through the log files and create a unique ID for each
1183 // entry
1184 std::vector<std::filesystem::path> redfishLogFiles;
1185 getRedfishLogFiles(redfishLogFiles);
1186 uint64_t entryCount = 0;
1187 std::string logEntry;
1188
1189 // Oldest logs are in the last file, so start there and loop
1190 // backwards
1191 for (auto it = redfishLogFiles.rbegin();
1192 it < redfishLogFiles.rend(); it++)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001193 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001194 std::ifstream logStream(*it);
1195 if (!logStream.is_open())
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001196 {
1197 continue;
1198 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001199
1200 // Reset the unique ID on the first entry
1201 bool firstEntry = true;
1202 while (std::getline(logStream, logEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001203 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001204 entryCount++;
1205 // Handle paging using skip (number of entries to skip
1206 // from the start) and top (number of entries to
1207 // display)
1208 if (entryCount <= skip || entryCount > skip + top)
George Liuebd45902020-08-26 14:21:10 +08001209 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001210 continue;
George Liuebd45902020-08-26 14:21:10 +08001211 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001212
1213 std::string idStr;
1214 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
George Liuebd45902020-08-26 14:21:10 +08001215 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001216 continue;
George Liuebd45902020-08-26 14:21:10 +08001217 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001218
1219 if (firstEntry)
1220 {
1221 firstEntry = false;
1222 }
1223
1224 logEntryArray.push_back({});
1225 nlohmann::json& bmcLogEntry = logEntryArray.back();
1226 if (fillEventLogEntryJson(idStr, logEntry,
1227 bmcLogEntry) != 0)
Xiaochao Ma75710de2021-01-21 17:56:02 +08001228 {
1229 messages::internalError(asyncResp->res);
1230 return;
1231 }
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001232 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001233 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001234 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1235 if (skip + top < entryCount)
Ed Tanous271584a2019-07-09 16:24:22 -07001236 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001237 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001238 "/redfish/v1/Systems/system/LogServices/EventLog/"
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001239 "Entries?$skip=" +
1240 std::to_string(skip + top);
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001241 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001242 });
1243}
Chicago Duan336e96c2019-07-15 14:22:08 +08001244
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001245inline void requestRoutesJournalEventLogEntry(App& app)
1246{
1247 BMCWEB_ROUTE(
1248 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001249 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001250 .methods(boost::beast::http::verb::get)(
1251 [](const crow::Request&,
1252 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1253 const std::string& param) {
1254 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001255
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001256 // Go through the log files and check the unique ID for each
1257 // entry to find the target entry
1258 std::vector<std::filesystem::path> redfishLogFiles;
1259 getRedfishLogFiles(redfishLogFiles);
1260 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001261
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001262 // Oldest logs are in the last file, so start there and loop
1263 // backwards
1264 for (auto it = redfishLogFiles.rbegin();
1265 it < redfishLogFiles.rend(); it++)
1266 {
1267 std::ifstream logStream(*it);
1268 if (!logStream.is_open())
1269 {
1270 continue;
1271 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001272
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001273 // Reset the unique ID on the first entry
1274 bool firstEntry = true;
1275 while (std::getline(logStream, logEntry))
1276 {
1277 std::string idStr;
1278 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1279 {
1280 continue;
1281 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001282
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001283 if (firstEntry)
1284 {
1285 firstEntry = false;
1286 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001287
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001288 if (idStr == targetID)
1289 {
1290 if (fillEventLogEntryJson(
1291 idStr, logEntry,
1292 asyncResp->res.jsonValue) != 0)
1293 {
1294 messages::internalError(asyncResp->res);
1295 return;
1296 }
1297 return;
1298 }
1299 }
1300 }
1301 // Requested ID was not found
1302 messages::resourceMissingAtURI(asyncResp->res, targetID);
1303 });
1304}
1305
1306inline void requestRoutesDBusEventLogEntryCollection(App& app)
1307{
1308 BMCWEB_ROUTE(app,
1309 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07001310 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001311 .methods(
1312 boost::beast::http::verb::
1313 get)([](const crow::Request&,
1314 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1315 // Collections don't include the static data added by SubRoute
1316 // because it has a duplicate entry for members
1317 asyncResp->res.jsonValue["@odata.type"] =
1318 "#LogEntryCollection.LogEntryCollection";
1319 asyncResp->res.jsonValue["@odata.id"] =
1320 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1321 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1322 asyncResp->res.jsonValue["Description"] =
1323 "Collection of System Event Log Entries";
1324
1325 // DBus implementation of EventLog/Entries
1326 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001327 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001328 [asyncResp](const boost::system::error_code ec,
1329 GetManagedObjectsType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001330 if (ec)
1331 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001332 // TODO Handle for specific error code
1333 BMCWEB_LOG_ERROR
1334 << "getLogEntriesIfaceData resp_handler got error "
1335 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001336 messages::internalError(asyncResp->res);
1337 return;
1338 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001339 nlohmann::json& entriesArray =
1340 asyncResp->res.jsonValue["Members"];
1341 entriesArray = nlohmann::json::array();
1342 for (auto& objectPath : resp)
1343 {
1344 uint32_t* id = nullptr;
1345 std::time_t timestamp{};
1346 std::time_t updateTimestamp{};
1347 std::string* severity = nullptr;
1348 std::string* message = nullptr;
1349 std::string* filePath = nullptr;
1350 bool resolved = false;
1351 for (auto& interfaceMap : objectPath.second)
1352 {
1353 if (interfaceMap.first ==
1354 "xyz.openbmc_project.Logging.Entry")
1355 {
1356 for (auto& propertyMap : interfaceMap.second)
1357 {
1358 if (propertyMap.first == "Id")
1359 {
1360 id = std::get_if<uint32_t>(
1361 &propertyMap.second);
1362 }
1363 else if (propertyMap.first == "Timestamp")
1364 {
1365 const uint64_t* millisTimeStamp =
1366 std::get_if<uint64_t>(
1367 &propertyMap.second);
1368 if (millisTimeStamp != nullptr)
1369 {
1370 timestamp =
1371 crow::utility::getTimestamp(
1372 *millisTimeStamp);
1373 }
1374 }
1375 else if (propertyMap.first ==
1376 "UpdateTimestamp")
1377 {
1378 const uint64_t* millisTimeStamp =
1379 std::get_if<uint64_t>(
1380 &propertyMap.second);
1381 if (millisTimeStamp != nullptr)
1382 {
1383 updateTimestamp =
1384 crow::utility::getTimestamp(
1385 *millisTimeStamp);
1386 }
1387 }
1388 else if (propertyMap.first == "Severity")
1389 {
1390 severity = std::get_if<std::string>(
1391 &propertyMap.second);
1392 }
1393 else if (propertyMap.first == "Message")
1394 {
1395 message = std::get_if<std::string>(
1396 &propertyMap.second);
1397 }
1398 else if (propertyMap.first == "Resolved")
1399 {
1400 bool* resolveptr = std::get_if<bool>(
1401 &propertyMap.second);
1402 if (resolveptr == nullptr)
1403 {
1404 messages::internalError(
1405 asyncResp->res);
1406 return;
1407 }
1408 resolved = *resolveptr;
1409 }
1410 }
1411 if (id == nullptr || message == nullptr ||
1412 severity == nullptr)
1413 {
1414 messages::internalError(asyncResp->res);
1415 return;
1416 }
1417 }
1418 else if (interfaceMap.first ==
1419 "xyz.openbmc_project.Common.FilePath")
1420 {
1421 for (auto& propertyMap : interfaceMap.second)
1422 {
1423 if (propertyMap.first == "Path")
1424 {
1425 filePath = std::get_if<std::string>(
1426 &propertyMap.second);
1427 }
1428 }
1429 }
1430 }
1431 // Object path without the
1432 // xyz.openbmc_project.Logging.Entry interface, ignore
1433 // and continue.
1434 if (id == nullptr || message == nullptr ||
1435 severity == nullptr)
1436 {
1437 continue;
1438 }
1439 entriesArray.push_back({});
1440 nlohmann::json& thisEntry = entriesArray.back();
1441 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1442 thisEntry["@odata.id"] =
1443 "/redfish/v1/Systems/system/"
1444 "LogServices/EventLog/Entries/" +
1445 std::to_string(*id);
1446 thisEntry["Name"] = "System Event Log Entry";
1447 thisEntry["Id"] = std::to_string(*id);
1448 thisEntry["Message"] = *message;
1449 thisEntry["Resolved"] = resolved;
1450 thisEntry["EntryType"] = "Event";
1451 thisEntry["Severity"] =
1452 translateSeverityDbusToRedfish(*severity);
1453 thisEntry["Created"] =
1454 crow::utility::getDateTime(timestamp);
1455 thisEntry["Modified"] =
1456 crow::utility::getDateTime(updateTimestamp);
1457 if (filePath != nullptr)
1458 {
1459 thisEntry["AdditionalDataURI"] =
1460 "/redfish/v1/Systems/system/LogServices/"
1461 "EventLog/"
1462 "Entries/" +
1463 std::to_string(*id) + "/attachment";
1464 }
1465 }
1466 std::sort(entriesArray.begin(), entriesArray.end(),
1467 [](const nlohmann::json& left,
1468 const nlohmann::json& right) {
1469 return (left["Id"] <= right["Id"]);
1470 });
1471 asyncResp->res.jsonValue["Members@odata.count"] =
1472 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001473 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001474 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1475 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1476 });
1477}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001478
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001479inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001480{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001481 BMCWEB_ROUTE(
1482 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001483 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001484 .methods(boost::beast::http::verb::get)(
1485 [](const crow::Request&,
1486 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1487 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001488
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001489 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001490 std::string entryID = param;
1491 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001492
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001493 // DBus implementation of EventLog/Entries
1494 // Make call to Logging Service to find all log entry objects
1495 crow::connections::systemBus->async_method_call(
1496 [asyncResp, entryID](const boost::system::error_code ec,
1497 GetManagedPropertyType& resp) {
1498 if (ec.value() == EBADR)
1499 {
1500 messages::resourceNotFound(
1501 asyncResp->res, "EventLogEntry", entryID);
1502 return;
1503 }
1504 if (ec)
1505 {
1506 BMCWEB_LOG_ERROR << "EventLogEntry (DBus) "
1507 "resp_handler got error "
1508 << ec;
1509 messages::internalError(asyncResp->res);
1510 return;
1511 }
1512 uint32_t* id = nullptr;
1513 std::time_t timestamp{};
1514 std::time_t updateTimestamp{};
1515 std::string* severity = nullptr;
1516 std::string* message = nullptr;
1517 std::string* filePath = nullptr;
1518 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001519
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001520 for (auto& propertyMap : resp)
1521 {
1522 if (propertyMap.first == "Id")
1523 {
1524 id = std::get_if<uint32_t>(&propertyMap.second);
1525 }
1526 else if (propertyMap.first == "Timestamp")
1527 {
1528 const uint64_t* millisTimeStamp =
1529 std::get_if<uint64_t>(&propertyMap.second);
1530 if (millisTimeStamp != nullptr)
1531 {
1532 timestamp = crow::utility::getTimestamp(
1533 *millisTimeStamp);
1534 }
1535 }
1536 else if (propertyMap.first == "UpdateTimestamp")
1537 {
1538 const uint64_t* millisTimeStamp =
1539 std::get_if<uint64_t>(&propertyMap.second);
1540 if (millisTimeStamp != nullptr)
1541 {
1542 updateTimestamp =
1543 crow::utility::getTimestamp(
1544 *millisTimeStamp);
1545 }
1546 }
1547 else if (propertyMap.first == "Severity")
1548 {
1549 severity = std::get_if<std::string>(
1550 &propertyMap.second);
1551 }
1552 else if (propertyMap.first == "Message")
1553 {
1554 message = std::get_if<std::string>(
1555 &propertyMap.second);
1556 }
1557 else if (propertyMap.first == "Resolved")
1558 {
1559 bool* resolveptr =
1560 std::get_if<bool>(&propertyMap.second);
1561 if (resolveptr == nullptr)
1562 {
1563 messages::internalError(asyncResp->res);
1564 return;
1565 }
1566 resolved = *resolveptr;
1567 }
1568 else if (propertyMap.first == "Path")
1569 {
1570 filePath = std::get_if<std::string>(
1571 &propertyMap.second);
1572 }
1573 }
1574 if (id == nullptr || message == nullptr ||
1575 severity == nullptr)
1576 {
1577 messages::internalError(asyncResp->res);
1578 return;
1579 }
1580 asyncResp->res.jsonValue["@odata.type"] =
1581 "#LogEntry.v1_8_0.LogEntry";
1582 asyncResp->res.jsonValue["@odata.id"] =
1583 "/redfish/v1/Systems/system/LogServices/EventLog/"
1584 "Entries/" +
1585 std::to_string(*id);
1586 asyncResp->res.jsonValue["Name"] =
1587 "System Event Log Entry";
1588 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1589 asyncResp->res.jsonValue["Message"] = *message;
1590 asyncResp->res.jsonValue["Resolved"] = resolved;
1591 asyncResp->res.jsonValue["EntryType"] = "Event";
1592 asyncResp->res.jsonValue["Severity"] =
1593 translateSeverityDbusToRedfish(*severity);
1594 asyncResp->res.jsonValue["Created"] =
1595 crow::utility::getDateTime(timestamp);
1596 asyncResp->res.jsonValue["Modified"] =
1597 crow::utility::getDateTime(updateTimestamp);
1598 if (filePath != nullptr)
1599 {
1600 asyncResp->res.jsonValue["AdditionalDataURI"] =
1601 "/redfish/v1/Systems/system/LogServices/"
1602 "EventLog/"
1603 "attachment/" +
1604 std::to_string(*id);
1605 }
1606 },
1607 "xyz.openbmc_project.Logging",
1608 "/xyz/openbmc_project/logging/entry/" + entryID,
1609 "org.freedesktop.DBus.Properties", "GetAll", "");
1610 });
1611
1612 BMCWEB_ROUTE(
1613 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001614 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001615 .methods(boost::beast::http::verb::patch)(
1616 [](const crow::Request& req,
1617 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1618 const std::string& entryId) {
1619 std::optional<bool> resolved;
1620
1621 if (!json_util::readJson(req, asyncResp->res, "Resolved",
1622 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001623 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001624 return;
1625 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001626 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001627
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001628 crow::connections::systemBus->async_method_call(
1629 [asyncResp, resolved,
1630 entryId](const boost::system::error_code ec) {
1631 if (ec)
1632 {
1633 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1634 messages::internalError(asyncResp->res);
1635 return;
1636 }
1637 },
1638 "xyz.openbmc_project.Logging",
1639 "/xyz/openbmc_project/logging/entry/" + entryId,
1640 "org.freedesktop.DBus.Properties", "Set",
1641 "xyz.openbmc_project.Logging.Entry", "Resolved",
1642 std::variant<bool>(*resolved));
1643 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001644
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001645 BMCWEB_ROUTE(
1646 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001647 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001648 .methods(boost::beast::http::verb::delete_)(
1649 [](const crow::Request&,
1650 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1651 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001652
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001653 {
1654 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001655
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001656 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001657
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001658 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001659
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660 // Process response from Logging service.
1661 auto respHandler = [asyncResp, entryID](
1662 const boost::system::error_code ec) {
1663 BMCWEB_LOG_DEBUG
1664 << "EventLogEntry (DBus) doDelete callback: Done";
1665 if (ec)
1666 {
1667 if (ec.value() == EBADR)
1668 {
1669 messages::resourceNotFound(asyncResp->res,
1670 "LogEntry", entryID);
1671 return;
1672 }
1673 // TODO Handle for specific error code
1674 BMCWEB_LOG_ERROR << "EventLogEntry (DBus) doDelete "
1675 "respHandler got error "
1676 << ec;
1677 asyncResp->res.result(
1678 boost::beast::http::status::internal_server_error);
1679 return;
1680 }
1681
1682 asyncResp->res.result(boost::beast::http::status::ok);
1683 };
1684
1685 // Make call to Logging service to request Delete Log
1686 crow::connections::systemBus->async_method_call(
1687 respHandler, "xyz.openbmc_project.Logging",
1688 "/xyz/openbmc_project/logging/entry/" + entryID,
1689 "xyz.openbmc_project.Object.Delete", "Delete");
1690 });
1691}
1692
1693inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001694{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001695 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/"
1696 "<str>/attachment")
Ed Tanous432a8902021-06-14 15:28:56 -07001697 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001698 .methods(boost::beast::http::verb::get)(
1699 [](const crow::Request& req,
1700 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1701 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001702
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001703 {
George Liuaf61db12021-03-08 19:36:32 +08001704 if (!http_helpers::isOctetAccepted(req))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001705 {
1706 asyncResp->res.result(
1707 boost::beast::http::status::bad_request);
1708 return;
1709 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001710
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001711 std::string entryID = param;
1712 dbus::utility::escapePathForDbus(entryID);
1713
1714 crow::connections::systemBus->async_method_call(
1715 [asyncResp,
1716 entryID](const boost::system::error_code ec,
1717 const sdbusplus::message::unix_fd& unixfd) {
1718 if (ec.value() == EBADR)
1719 {
1720 messages::resourceNotFound(
1721 asyncResp->res, "EventLogAttachment", entryID);
1722 return;
1723 }
1724 if (ec)
1725 {
1726 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1727 messages::internalError(asyncResp->res);
1728 return;
1729 }
1730
1731 int fd = -1;
1732 fd = dup(unixfd);
1733 if (fd == -1)
1734 {
1735 messages::internalError(asyncResp->res);
1736 return;
1737 }
1738
1739 long long int size = lseek(fd, 0, SEEK_END);
1740 if (size == -1)
1741 {
1742 messages::internalError(asyncResp->res);
1743 return;
1744 }
1745
1746 // Arbitrary max size of 64kb
1747 constexpr int maxFileSize = 65536;
1748 if (size > maxFileSize)
1749 {
1750 BMCWEB_LOG_ERROR
1751 << "File size exceeds maximum allowed size of "
1752 << maxFileSize;
1753 messages::internalError(asyncResp->res);
1754 return;
1755 }
1756 std::vector<char> data(static_cast<size_t>(size));
1757 long long int rc = lseek(fd, 0, SEEK_SET);
1758 if (rc == -1)
1759 {
1760 messages::internalError(asyncResp->res);
1761 return;
1762 }
1763 rc = read(fd, data.data(), data.size());
1764 if ((rc == -1) || (rc != size))
1765 {
1766 messages::internalError(asyncResp->res);
1767 return;
1768 }
1769 close(fd);
1770
1771 std::string_view strData(data.data(), data.size());
1772 std::string output =
1773 crow::utility::base64encode(strData);
1774
1775 asyncResp->res.addHeader("Content-Type",
1776 "application/octet-stream");
1777 asyncResp->res.addHeader("Content-Transfer-Encoding",
1778 "Base64");
1779 asyncResp->res.body() = std::move(output);
1780 },
1781 "xyz.openbmc_project.Logging",
1782 "/xyz/openbmc_project/logging/entry/" + entryID,
1783 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1784 });
1785}
1786
1787inline void requestRoutesBMCLogServiceCollection(App& app)
1788{
1789 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous432a8902021-06-14 15:28:56 -07001790 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001791 .methods(boost::beast::http::verb::get)(
1792 [](const crow::Request&,
1793 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1794 // Collections don't include the static data added by SubRoute
1795 // because it has a duplicate entry for members
1796 asyncResp->res.jsonValue["@odata.type"] =
1797 "#LogServiceCollection.LogServiceCollection";
1798 asyncResp->res.jsonValue["@odata.id"] =
1799 "/redfish/v1/Managers/bmc/LogServices";
1800 asyncResp->res.jsonValue["Name"] =
1801 "Open BMC Log Services Collection";
1802 asyncResp->res.jsonValue["Description"] =
1803 "Collection of LogServices for this Manager";
1804 nlohmann::json& logServiceArray =
1805 asyncResp->res.jsonValue["Members"];
1806 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001807#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001808 logServiceArray.push_back(
1809 {{"@odata.id",
1810 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001811#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001812#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001813 logServiceArray.push_back(
1814 {{"@odata.id",
1815 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001816#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001817 asyncResp->res.jsonValue["Members@odata.count"] =
1818 logServiceArray.size();
1819 });
1820}
Ed Tanous1da66f72018-07-27 16:13:37 -07001821
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001822inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07001823{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001824 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanous432a8902021-06-14 15:28:56 -07001825 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001826 .methods(boost::beast::http::verb::get)(
1827 [](const crow::Request&,
1828 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07001829
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001830 {
1831 asyncResp->res.jsonValue["@odata.type"] =
1832 "#LogService.v1_1_0.LogService";
1833 asyncResp->res.jsonValue["@odata.id"] =
1834 "/redfish/v1/Managers/bmc/LogServices/Journal";
1835 asyncResp->res.jsonValue["Name"] =
1836 "Open BMC Journal Log Service";
1837 asyncResp->res.jsonValue["Description"] =
1838 "BMC Journal Log Service";
1839 asyncResp->res.jsonValue["Id"] = "BMC Journal";
1840 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301841
1842 std::pair<std::string, std::string> redfishDateTimeOffset =
1843 crow::utility::getDateTimeOffsetNow();
1844 asyncResp->res.jsonValue["DateTime"] =
1845 redfishDateTimeOffset.first;
1846 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1847 redfishDateTimeOffset.second;
1848
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001849 asyncResp->res.jsonValue["Entries"] = {
1850 {"@odata.id",
1851 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
1852 });
1853}
Jason M. Billse1f26342018-07-18 12:12:00 -07001854
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001855static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1856 sd_journal* journal,
1857 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001858{
1859 // Get the Log Entry contents
1860 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001861
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001862 std::string message;
1863 std::string_view syslogID;
1864 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
1865 if (ret < 0)
1866 {
1867 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
1868 << strerror(-ret);
1869 }
1870 if (!syslogID.empty())
1871 {
1872 message += std::string(syslogID) + ": ";
1873 }
1874
Ed Tanous39e77502019-03-04 17:35:53 -08001875 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001876 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001877 if (ret < 0)
1878 {
1879 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1880 return 1;
1881 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001882 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001883
1884 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001885 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001886 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001887 if (ret < 0)
1888 {
1889 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001890 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001891
1892 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001893 std::string entryTimeStr;
1894 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001895 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001896 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001897 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001898
1899 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001900 bmcJournalLogEntryJson = {
George Liu0ef217f2021-03-08 14:24:46 +08001901 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001902 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1903 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001904 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001905 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001906 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07001907 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06001908 {"Severity", severity <= 2 ? "Critical"
1909 : severity <= 4 ? "Warning"
1910 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001911 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001912 {"Created", std::move(entryTimeStr)}};
1913 return 0;
1914}
1915
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001916inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07001917{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001918 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07001919 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001920 .methods(boost::beast::http::verb::get)(
1921 [](const crow::Request& req,
1922 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1923 static constexpr const long maxEntriesPerPage = 1000;
1924 uint64_t skip = 0;
1925 uint64_t top = maxEntriesPerPage; // Show max entries by default
1926 if (!getSkipParam(asyncResp, req, skip))
1927 {
1928 return;
1929 }
1930 if (!getTopParam(asyncResp, req, top))
1931 {
1932 return;
1933 }
1934 // Collections don't include the static data added by SubRoute
1935 // because it has a duplicate entry for members
1936 asyncResp->res.jsonValue["@odata.type"] =
1937 "#LogEntryCollection.LogEntryCollection";
1938 asyncResp->res.jsonValue["@odata.id"] =
1939 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1940 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1941 asyncResp->res.jsonValue["Description"] =
1942 "Collection of BMC Journal Entries";
1943 nlohmann::json& logEntryArray =
1944 asyncResp->res.jsonValue["Members"];
1945 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07001946
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001947 // Go through the journal and use the timestamp to create a
1948 // unique ID for each entry
1949 sd_journal* journalTmp = nullptr;
1950 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1951 if (ret < 0)
1952 {
1953 BMCWEB_LOG_ERROR << "failed to open journal: "
1954 << strerror(-ret);
1955 messages::internalError(asyncResp->res);
1956 return;
1957 }
1958 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
1959 journal(journalTmp, sd_journal_close);
1960 journalTmp = nullptr;
1961 uint64_t entryCount = 0;
1962 // Reset the unique ID on the first entry
1963 bool firstEntry = true;
1964 SD_JOURNAL_FOREACH(journal.get())
1965 {
1966 entryCount++;
1967 // Handle paging using skip (number of entries to skip from
1968 // the start) and top (number of entries to display)
1969 if (entryCount <= skip || entryCount > skip + top)
1970 {
1971 continue;
1972 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001973
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001974 std::string idStr;
1975 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1976 {
1977 continue;
1978 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001979
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001980 if (firstEntry)
1981 {
1982 firstEntry = false;
1983 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001984
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001985 logEntryArray.push_back({});
1986 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
1987 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1988 bmcJournalLogEntry) != 0)
1989 {
1990 messages::internalError(asyncResp->res);
1991 return;
1992 }
1993 }
1994 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1995 if (skip + top < entryCount)
1996 {
1997 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1998 "/redfish/v1/Managers/bmc/LogServices/Journal/"
1999 "Entries?$skip=" +
2000 std::to_string(skip + top);
2001 }
2002 });
2003}
Jason M. Billse1f26342018-07-18 12:12:00 -07002004
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002005inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002006{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002007 BMCWEB_ROUTE(app,
2008 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002009 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002010 .methods(boost::beast::http::verb::get)(
2011 [](const crow::Request&,
2012 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2013 const std::string& entryID) {
2014 // Convert the unique ID back to a timestamp to find the entry
2015 uint64_t ts = 0;
2016 uint64_t index = 0;
2017 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2018 {
2019 return;
2020 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002021
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002022 sd_journal* journalTmp = nullptr;
2023 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2024 if (ret < 0)
2025 {
2026 BMCWEB_LOG_ERROR << "failed to open journal: "
2027 << strerror(-ret);
2028 messages::internalError(asyncResp->res);
2029 return;
2030 }
2031 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2032 journal(journalTmp, sd_journal_close);
2033 journalTmp = nullptr;
2034 // Go to the timestamp in the log and move to the entry at the
2035 // index tracking the unique ID
2036 std::string idStr;
2037 bool firstEntry = true;
2038 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2039 if (ret < 0)
2040 {
2041 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2042 << strerror(-ret);
2043 messages::internalError(asyncResp->res);
2044 return;
2045 }
2046 for (uint64_t i = 0; i <= index; i++)
2047 {
2048 sd_journal_next(journal.get());
2049 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2050 {
2051 messages::internalError(asyncResp->res);
2052 return;
2053 }
2054 if (firstEntry)
2055 {
2056 firstEntry = false;
2057 }
2058 }
2059 // Confirm that the entry ID matches what was requested
2060 if (idStr != entryID)
2061 {
2062 messages::resourceMissingAtURI(asyncResp->res, entryID);
2063 return;
2064 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002065
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002066 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2067 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002068 {
2069 messages::internalError(asyncResp->res);
2070 return;
2071 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002072 });
2073}
2074
2075inline void requestRoutesBMCDumpService(App& app)
2076{
2077 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanous432a8902021-06-14 15:28:56 -07002078 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002079 .methods(boost::beast::http::verb::get)(
2080 [](const crow::Request&,
2081 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2082 asyncResp->res.jsonValue["@odata.id"] =
2083 "/redfish/v1/Managers/bmc/LogServices/Dump";
2084 asyncResp->res.jsonValue["@odata.type"] =
2085 "#LogService.v1_2_0.LogService";
2086 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2087 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2088 asyncResp->res.jsonValue["Id"] = "Dump";
2089 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302090
2091 std::pair<std::string, std::string> redfishDateTimeOffset =
2092 crow::utility::getDateTimeOffsetNow();
2093 asyncResp->res.jsonValue["DateTime"] =
2094 redfishDateTimeOffset.first;
2095 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2096 redfishDateTimeOffset.second;
2097
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002098 asyncResp->res.jsonValue["Entries"] = {
2099 {"@odata.id",
2100 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2101 asyncResp->res.jsonValue["Actions"] = {
2102 {"#LogService.ClearLog",
2103 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2104 "Actions/LogService.ClearLog"}}},
2105 {"#LogService.CollectDiagnosticData",
2106 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2107 "Actions/LogService.CollectDiagnosticData"}}}};
2108 });
2109}
2110
2111inline void requestRoutesBMCDumpEntryCollection(App& app)
2112{
2113
2114 /**
2115 * Functions triggers appropriate requests on DBus
2116 */
2117 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07002118 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002119 .methods(boost::beast::http::verb::get)(
2120 [](const crow::Request&,
2121 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2122 asyncResp->res.jsonValue["@odata.type"] =
2123 "#LogEntryCollection.LogEntryCollection";
2124 asyncResp->res.jsonValue["@odata.id"] =
2125 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2126 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2127 asyncResp->res.jsonValue["Description"] =
2128 "Collection of BMC Dump Entries";
2129
2130 getDumpEntryCollection(asyncResp, "BMC");
2131 });
2132}
2133
2134inline void requestRoutesBMCDumpEntry(App& app)
2135{
2136 BMCWEB_ROUTE(app,
2137 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002138 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002139 .methods(boost::beast::http::verb::get)(
2140 [](const crow::Request&,
2141 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2142 const std::string& param) {
2143 getDumpEntryById(asyncResp, param, "BMC");
2144 });
2145 BMCWEB_ROUTE(app,
2146 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002147 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002148 .methods(boost::beast::http::verb::delete_)(
2149 [](const crow::Request&,
2150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2151 const std::string& param) {
2152 deleteDumpEntry(asyncResp, param, "bmc");
2153 });
2154}
2155
2156inline void requestRoutesBMCDumpCreate(App& app)
2157{
2158
2159 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2160 "Actions/"
2161 "LogService.CollectDiagnosticData/")
Ed Tanous432a8902021-06-14 15:28:56 -07002162 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002163 .methods(boost::beast::http::verb::post)(
2164 [](const crow::Request& req,
2165 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2166 createDump(asyncResp, req, "BMC");
2167 });
2168}
2169
2170inline void requestRoutesBMCDumpClear(App& app)
2171{
2172 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2173 "Actions/"
2174 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002175 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002176 .methods(boost::beast::http::verb::post)(
2177 [](const crow::Request&,
2178 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2179 clearDump(asyncResp, "BMC");
2180 });
2181}
2182
2183inline void requestRoutesSystemDumpService(App& app)
2184{
2185 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanous432a8902021-06-14 15:28:56 -07002186 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002187 .methods(boost::beast::http::verb::get)(
2188 [](const crow::Request&,
2189 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2190
2191 {
2192 asyncResp->res.jsonValue["@odata.id"] =
2193 "/redfish/v1/Systems/system/LogServices/Dump";
2194 asyncResp->res.jsonValue["@odata.type"] =
2195 "#LogService.v1_2_0.LogService";
2196 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2197 asyncResp->res.jsonValue["Description"] =
2198 "System Dump LogService";
2199 asyncResp->res.jsonValue["Id"] = "Dump";
2200 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302201
2202 std::pair<std::string, std::string> redfishDateTimeOffset =
2203 crow::utility::getDateTimeOffsetNow();
2204 asyncResp->res.jsonValue["DateTime"] =
2205 redfishDateTimeOffset.first;
2206 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2207 redfishDateTimeOffset.second;
2208
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002209 asyncResp->res.jsonValue["Entries"] = {
2210 {"@odata.id",
2211 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2212 asyncResp->res.jsonValue["Actions"] = {
2213 {"#LogService.ClearLog",
2214 {{"target",
2215 "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2216 "LogService.ClearLog"}}},
2217 {"#LogService.CollectDiagnosticData",
2218 {{"target",
2219 "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2220 "LogService.CollectDiagnosticData"}}}};
2221 });
2222}
2223
2224inline void requestRoutesSystemDumpEntryCollection(App& app)
2225{
2226
2227 /**
2228 * Functions triggers appropriate requests on DBus
2229 */
Charles Boyerf9a67082021-06-15 08:29:29 -05002230 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07002231 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002232 .methods(boost::beast::http::verb::get)(
2233 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002234 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002235 asyncResp->res.jsonValue["@odata.type"] =
2236 "#LogEntryCollection.LogEntryCollection";
2237 asyncResp->res.jsonValue["@odata.id"] =
2238 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2239 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2240 asyncResp->res.jsonValue["Description"] =
2241 "Collection of System Dump Entries";
2242
2243 getDumpEntryCollection(asyncResp, "System");
2244 });
2245}
2246
2247inline void requestRoutesSystemDumpEntry(App& app)
2248{
2249 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002250 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002251 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002252 .methods(boost::beast::http::verb::get)(
2253 [](const crow::Request&,
2254 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2255 const std::string& param) {
2256 getDumpEntryById(asyncResp, param, "System");
2257 });
2258
2259 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002260 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002261 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002262 .methods(boost::beast::http::verb::delete_)(
2263 [](const crow::Request&,
2264 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2265 const std::string& param) {
2266 deleteDumpEntry(asyncResp, param, "system");
2267 });
2268}
2269
2270inline void requestRoutesSystemDumpCreate(App& app)
2271{
2272 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2273 "Actions/"
2274 "LogService.CollectDiagnosticData/")
Ed Tanous432a8902021-06-14 15:28:56 -07002275 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002276 .methods(boost::beast::http::verb::post)(
2277 [](const crow::Request& req,
2278 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2279
2280 { createDump(asyncResp, req, "System"); });
2281}
2282
2283inline void requestRoutesSystemDumpClear(App& app)
2284{
2285 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2286 "Actions/"
2287 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002288 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002289 .methods(boost::beast::http::verb::post)(
2290 [](const crow::Request&,
2291 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2292
2293 { clearDump(asyncResp, "System"); });
2294}
2295
2296inline void requestRoutesCrashdumpService(App& app)
2297{
2298 // Note: Deviated from redfish privilege registry for GET & HEAD
2299 // method for security reasons.
2300 /**
2301 * Functions triggers appropriate requests on DBus
2302 */
2303 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous432a8902021-06-14 15:28:56 -07002304 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002305 .methods(
2306 boost::beast::http::verb::
2307 get)([](const crow::Request&,
2308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2309 // Copy over the static data to include the entries added by
2310 // SubRoute
2311 asyncResp->res.jsonValue["@odata.id"] =
2312 "/redfish/v1/Systems/system/LogServices/Crashdump";
2313 asyncResp->res.jsonValue["@odata.type"] =
2314 "#LogService.v1_2_0.LogService";
2315 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2316 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2317 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2318 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2319 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302320
2321 std::pair<std::string, std::string> redfishDateTimeOffset =
2322 crow::utility::getDateTimeOffsetNow();
2323 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2324 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2325 redfishDateTimeOffset.second;
2326
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002327 asyncResp->res.jsonValue["Entries"] = {
2328 {"@odata.id",
2329 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2330 asyncResp->res.jsonValue["Actions"] = {
2331 {"#LogService.ClearLog",
2332 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2333 "Actions/LogService.ClearLog"}}},
2334 {"#LogService.CollectDiagnosticData",
2335 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2336 "Actions/LogService.CollectDiagnosticData"}}}};
2337 });
2338}
2339
2340void inline requestRoutesCrashdumpClear(App& app)
2341{
2342 BMCWEB_ROUTE(app,
2343 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2344 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002345 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002346 .methods(boost::beast::http::verb::post)(
2347 [](const crow::Request&,
2348 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2349 crow::connections::systemBus->async_method_call(
2350 [asyncResp](const boost::system::error_code ec,
2351 const std::string&) {
2352 if (ec)
2353 {
2354 messages::internalError(asyncResp->res);
2355 return;
2356 }
2357 messages::success(asyncResp->res);
2358 },
2359 crashdumpObject, crashdumpPath, deleteAllInterface,
2360 "DeleteAll");
2361 });
2362}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002363
zhanghch058d1b46d2021-04-01 11:18:24 +08002364static void
2365 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2366 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002367{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002368 auto getStoredLogCallback =
2369 [asyncResp, logID, &logEntryJson](
2370 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002371 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002372 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002373 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002374 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2375 if (ec.value() ==
2376 boost::system::linux_error::bad_request_descriptor)
2377 {
2378 messages::resourceNotFound(asyncResp->res, "LogEntry",
2379 logID);
2380 }
2381 else
2382 {
2383 messages::internalError(asyncResp->res);
2384 }
2385 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002386 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002387
Johnathan Mantey043a0532020-03-10 17:15:28 -07002388 std::string timestamp{};
2389 std::string filename{};
2390 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002391 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002392
2393 if (filename.empty() || timestamp.empty())
2394 {
2395 messages::resourceMissingAtURI(asyncResp->res, logID);
2396 return;
2397 }
2398
2399 std::string crashdumpURI =
2400 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2401 logID + "/" + filename;
George Liu0ef217f2021-03-08 14:24:46 +08002402 logEntryJson = {{"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002403 {"@odata.id", "/redfish/v1/Systems/system/"
2404 "LogServices/Crashdump/Entries/" +
2405 logID},
2406 {"Name", "CPU Crashdump"},
2407 {"Id", logID},
2408 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002409 {"AdditionalDataURI", std::move(crashdumpURI)},
2410 {"DiagnosticDataType", "OEM"},
2411 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002412 {"Created", std::move(timestamp)}};
2413 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002414 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002415 std::move(getStoredLogCallback), crashdumpObject,
2416 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002417 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002418}
2419
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002420inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002421{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002422 // Note: Deviated from redfish privilege registry for GET & HEAD
2423 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002424 /**
2425 * Functions triggers appropriate requests on DBus
2426 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002427 BMCWEB_ROUTE(app,
2428 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07002429 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002430 .methods(
2431 boost::beast::http::verb::
2432 get)([](const crow::Request&,
2433 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2434 // Collections don't include the static data added by SubRoute
2435 // because it has a duplicate entry for members
2436 auto getLogEntriesCallback = [asyncResp](
2437 const boost::system::error_code ec,
2438 const std::vector<std::string>&
2439 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002440 if (ec)
2441 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002442 if (ec.value() !=
2443 boost::system::errc::no_such_file_or_directory)
2444 {
2445 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2446 << ec.message();
2447 messages::internalError(asyncResp->res);
2448 return;
2449 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002450 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002451 asyncResp->res.jsonValue["@odata.type"] =
2452 "#LogEntryCollection.LogEntryCollection";
2453 asyncResp->res.jsonValue["@odata.id"] =
2454 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2455 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2456 asyncResp->res.jsonValue["Description"] =
2457 "Collection of Crashdump Entries";
2458 nlohmann::json& logEntryArray =
2459 asyncResp->res.jsonValue["Members"];
2460 logEntryArray = nlohmann::json::array();
2461 std::vector<std::string> logIDs;
2462 // Get the list of log entries and build up an empty array big
2463 // enough to hold them
2464 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002465 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002466 // Get the log ID
2467 std::size_t lastPos = objpath.rfind('/');
2468 if (lastPos == std::string::npos)
2469 {
2470 continue;
2471 }
2472 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002473
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002474 // Add a space for the log entry to the array
2475 logEntryArray.push_back({});
2476 }
2477 // Now go through and set up async calls to fill in the entries
2478 size_t index = 0;
2479 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002480 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002481 // Add the log entry to the array
2482 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002483 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002484 asyncResp->res.jsonValue["Members@odata.count"] =
2485 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002486 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002487 crow::connections::systemBus->async_method_call(
2488 std::move(getLogEntriesCallback),
2489 "xyz.openbmc_project.ObjectMapper",
2490 "/xyz/openbmc_project/object_mapper",
2491 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2492 std::array<const char*, 1>{crashdumpInterface});
2493 });
2494}
Ed Tanous1da66f72018-07-27 16:13:37 -07002495
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002496inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002497{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002498 // Note: Deviated from redfish privilege registry for GET & HEAD
2499 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002500
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002501 BMCWEB_ROUTE(
2502 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002503 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002504 .methods(boost::beast::http::verb::get)(
2505 [](const crow::Request&,
2506 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2507 const std::string& param) {
2508 const std::string& logID = param;
2509 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2510 });
2511}
Ed Tanous1da66f72018-07-27 16:13:37 -07002512
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002513inline void requestRoutesCrashdumpFile(App& app)
2514{
2515 // Note: Deviated from redfish privilege registry for GET & HEAD
2516 // method for security reasons.
2517 BMCWEB_ROUTE(
2518 app,
2519 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002520 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002521 .methods(boost::beast::http::verb::get)(
2522 [](const crow::Request&,
2523 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2524 const std::string& logID, const std::string& fileName) {
2525 auto getStoredLogCallback =
2526 [asyncResp, logID, fileName](
2527 const boost::system::error_code ec,
2528 const std::vector<std::pair<std::string, VariantType>>&
2529 resp) {
2530 if (ec)
2531 {
2532 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2533 << ec.message();
2534 messages::internalError(asyncResp->res);
2535 return;
2536 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002537
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002538 std::string dbusFilename{};
2539 std::string dbusTimestamp{};
2540 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002541
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002542 parseCrashdumpParameters(resp, dbusFilename,
2543 dbusTimestamp, dbusFilepath);
2544
2545 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2546 dbusFilepath.empty())
2547 {
2548 messages::resourceMissingAtURI(asyncResp->res,
2549 fileName);
2550 return;
2551 }
2552
2553 // Verify the file name parameter is correct
2554 if (fileName != dbusFilename)
2555 {
2556 messages::resourceMissingAtURI(asyncResp->res,
2557 fileName);
2558 return;
2559 }
2560
2561 if (!std::filesystem::exists(dbusFilepath))
2562 {
2563 messages::resourceMissingAtURI(asyncResp->res,
2564 fileName);
2565 return;
2566 }
2567 std::ifstream ifs(dbusFilepath, std::ios::in |
2568 std::ios::binary |
2569 std::ios::ate);
2570 std::ifstream::pos_type fileSize = ifs.tellg();
2571 if (fileSize < 0)
2572 {
2573 messages::generalError(asyncResp->res);
2574 return;
2575 }
2576 ifs.seekg(0, std::ios::beg);
2577
2578 auto crashData = std::make_unique<char[]>(
2579 static_cast<unsigned int>(fileSize));
2580
2581 ifs.read(crashData.get(), static_cast<int>(fileSize));
2582
2583 // The cast to std::string is intentional in order to
2584 // use the assign() that applies move mechanics
2585 asyncResp->res.body().assign(
2586 static_cast<std::string>(crashData.get()));
2587
2588 // Configure this to be a file download when accessed
2589 // from a browser
2590 asyncResp->res.addHeader("Content-Disposition",
2591 "attachment");
2592 };
2593 crow::connections::systemBus->async_method_call(
2594 std::move(getStoredLogCallback), crashdumpObject,
2595 crashdumpPath + std::string("/") + logID,
2596 "org.freedesktop.DBus.Properties", "GetAll",
2597 crashdumpInterface);
2598 });
2599}
2600
2601inline void requestRoutesCrashdumpCollect(App& app)
2602{
2603 // Note: Deviated from redfish privilege registry for GET & HEAD
2604 // method for security reasons.
2605 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/"
2606 "Actions/LogService.CollectDiagnosticData/")
Ed Tanous432a8902021-06-14 15:28:56 -07002607 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002608 .methods(
2609 boost::beast::http::verb::
2610 post)([](const crow::Request& req,
2611 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2612 std::string diagnosticDataType;
2613 std::string oemDiagnosticDataType;
2614 if (!redfish::json_util::readJson(
2615 req, asyncResp->res, "DiagnosticDataType",
2616 diagnosticDataType, "OEMDiagnosticDataType",
2617 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002618 {
James Feist46229572020-02-19 15:11:58 -08002619 return;
2620 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002621
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002622 if (diagnosticDataType != "OEM")
2623 {
2624 BMCWEB_LOG_ERROR
2625 << "Only OEM DiagnosticDataType supported for Crashdump";
2626 messages::actionParameterValueFormatError(
2627 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2628 "CollectDiagnosticData");
2629 return;
2630 }
2631
2632 auto collectCrashdumpCallback = [asyncResp, req](
2633 const boost::system::error_code
2634 ec,
2635 const std::string&) {
2636 if (ec)
2637 {
2638 if (ec.value() ==
2639 boost::system::errc::operation_not_supported)
2640 {
2641 messages::resourceInStandby(asyncResp->res);
2642 }
2643 else if (ec.value() ==
2644 boost::system::errc::device_or_resource_busy)
2645 {
2646 messages::serviceTemporarilyUnavailable(asyncResp->res,
2647 "60");
2648 }
2649 else
2650 {
2651 messages::internalError(asyncResp->res);
2652 }
2653 return;
2654 }
2655 std::shared_ptr<task::TaskData> task =
2656 task::TaskData::createTask(
2657 [](boost::system::error_code err,
2658 sdbusplus::message::message&,
2659 const std::shared_ptr<task::TaskData>& taskData) {
2660 if (!err)
2661 {
2662 taskData->messages.emplace_back(
2663 messages::taskCompletedOK(
2664 std::to_string(taskData->index)));
2665 taskData->state = "Completed";
2666 }
2667 return task::completed;
2668 },
2669 "type='signal',interface='org.freedesktop.DBus."
2670 "Properties',"
2671 "member='PropertiesChanged',arg0namespace='com.intel."
2672 "crashdump'");
2673 task->startTimer(std::chrono::minutes(5));
2674 task->populateResp(asyncResp->res);
2675 task->payload.emplace(req);
2676 };
2677
2678 if (oemDiagnosticDataType == "OnDemand")
2679 {
2680 crow::connections::systemBus->async_method_call(
2681 std::move(collectCrashdumpCallback), crashdumpObject,
2682 crashdumpPath, crashdumpOnDemandInterface,
2683 "GenerateOnDemandLog");
2684 }
2685 else if (oemDiagnosticDataType == "Telemetry")
2686 {
2687 crow::connections::systemBus->async_method_call(
2688 std::move(collectCrashdumpCallback), crashdumpObject,
2689 crashdumpPath, crashdumpTelemetryInterface,
2690 "GenerateTelemetryLog");
2691 }
2692 else
2693 {
2694 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2695 << oemDiagnosticDataType;
2696 messages::actionParameterValueFormatError(
2697 asyncResp->res, oemDiagnosticDataType,
2698 "OEMDiagnosticDataType", "CollectDiagnosticData");
2699 return;
2700 }
2701 });
2702}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002703
Andrew Geisslercb92c032018-08-17 07:56:14 -07002704/**
2705 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2706 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002707inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002708{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002709 /**
2710 * Function handles POST method request.
2711 * The Clear Log actions does not require any parameter.The action deletes
2712 * all entries found in the Entries collection for this Log Service.
2713 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002714
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002715 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
2716 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002717 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002718 .methods(boost::beast::http::verb::post)(
2719 [](const crow::Request&,
2720 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2721 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07002722
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002723 // Process response from Logging service.
2724 auto respHandler = [asyncResp](
2725 const boost::system::error_code ec) {
2726 BMCWEB_LOG_DEBUG
2727 << "doClearLog resp_handler callback: Done";
2728 if (ec)
2729 {
2730 // TODO Handle for specific error code
2731 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
2732 << ec;
2733 asyncResp->res.result(
2734 boost::beast::http::status::internal_server_error);
2735 return;
2736 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07002737
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002738 asyncResp->res.result(
2739 boost::beast::http::status::no_content);
2740 };
2741
2742 // Make call to Logging service to request Clear Log
2743 crow::connections::systemBus->async_method_call(
2744 respHandler, "xyz.openbmc_project.Logging",
2745 "/xyz/openbmc_project/logging",
2746 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2747 });
2748}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002749
2750/****************************************************
2751 * Redfish PostCode interfaces
2752 * using DBUS interface: getPostCodesTS
2753 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002754inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002755{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002756 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanous432a8902021-06-14 15:28:56 -07002757 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002758 .methods(boost::beast::http::verb::get)(
2759 [](const crow::Request&,
2760 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2761 asyncResp->res.jsonValue = {
2762 {"@odata.id",
2763 "/redfish/v1/Systems/system/LogServices/PostCodes"},
2764 {"@odata.type", "#LogService.v1_1_0.LogService"},
2765 {"Name", "POST Code Log Service"},
2766 {"Description", "POST Code Log Service"},
2767 {"Id", "BIOS POST Code Log"},
2768 {"OverWritePolicy", "WrapsWhenFull"},
2769 {"Entries",
2770 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2771 "PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05302772
2773 std::pair<std::string, std::string> redfishDateTimeOffset =
2774 crow::utility::getDateTimeOffsetNow();
2775 asyncResp->res.jsonValue["DateTime"] =
2776 redfishDateTimeOffset.first;
2777 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2778 redfishDateTimeOffset.second;
2779
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002780 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2781 {"target",
2782 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2783 "Actions/LogService.ClearLog"}};
2784 });
2785}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002786
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002787inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002788{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002789 BMCWEB_ROUTE(app,
2790 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2791 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002792 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002793 .methods(boost::beast::http::verb::post)(
2794 [](const crow::Request&,
2795 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2796 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08002797
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002798 // Make call to post-code service to request clear all
2799 crow::connections::systemBus->async_method_call(
2800 [asyncResp](const boost::system::error_code ec) {
2801 if (ec)
2802 {
2803 // TODO Handle for specific error code
2804 BMCWEB_LOG_ERROR
2805 << "doClearPostCodes resp_handler got error "
2806 << ec;
2807 asyncResp->res.result(boost::beast::http::status::
2808 internal_server_error);
2809 messages::internalError(asyncResp->res);
2810 return;
2811 }
2812 },
2813 "xyz.openbmc_project.State.Boot.PostCode0",
2814 "/xyz/openbmc_project/State/Boot/PostCode0",
2815 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2816 });
2817}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002818
2819static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08002820 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302821 const boost::container::flat_map<
2822 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002823 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2824 const uint64_t skip = 0, const uint64_t top = 0)
2825{
2826 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002827 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05302828 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08002829
2830 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002831 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08002832
2833 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302834 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2835 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002836 {
2837 currentCodeIndex++;
2838 std::string postcodeEntryID =
2839 "B" + std::to_string(bootIndex) + "-" +
2840 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2841
2842 uint64_t usecSinceEpoch = code.first;
2843 uint64_t usTimeOffset = 0;
2844
2845 if (1 == currentCodeIndex)
2846 { // already incremented
2847 firstCodeTimeUs = code.first;
2848 }
2849 else
2850 {
2851 usTimeOffset = code.first - firstCodeTimeUs;
2852 }
2853
2854 // skip if no specific codeIndex is specified and currentCodeIndex does
2855 // not fall between top and skip
2856 if ((codeIndex == 0) &&
2857 (currentCodeIndex <= skip || currentCodeIndex > top))
2858 {
2859 continue;
2860 }
2861
Gunnar Mills4e0453b2020-07-08 14:00:30 -05002862 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08002863 // currentIndex
2864 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2865 {
2866 // This is done for simplicity. 1st entry is needed to calculate
2867 // time offset. To improve efficiency, one can get to the entry
2868 // directly (possibly with flatmap's nth method)
2869 continue;
2870 }
2871
2872 // currentCodeIndex is within top and skip or equal to specified code
2873 // index
2874
2875 // Get the Created time from the timestamp
2876 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05002877 entryTimeStr = crow::utility::getDateTime(
2878 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08002879
2880 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2881 std::ostringstream hexCode;
2882 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302883 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08002884 std::ostringstream timeOffsetStr;
2885 // Set Fixed -Point Notation
2886 timeOffsetStr << std::fixed;
2887 // Set precision to 4 digits
2888 timeOffsetStr << std::setprecision(4);
2889 // Add double to stream
2890 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2891 std::vector<std::string> messageArgs = {
2892 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2893
2894 // Get MessageArgs template from message registry
2895 std::string msg;
2896 if (message != nullptr)
2897 {
2898 msg = message->message;
2899
2900 // fill in this post code value
2901 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002902 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002903 {
2904 std::string argStr = "%" + std::to_string(++i);
2905 size_t argPos = msg.find(argStr);
2906 if (argPos != std::string::npos)
2907 {
2908 msg.replace(argPos, argStr.length(), messageArg);
2909 }
2910 }
2911 }
2912
Tim Leed4342a92020-04-27 11:47:58 +08002913 // Get Severity template from message registry
2914 std::string severity;
2915 if (message != nullptr)
2916 {
2917 severity = message->severity;
2918 }
2919
ZhikuiRena3316fc2020-01-29 14:58:08 -08002920 // add to AsyncResp
2921 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002922 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0ef217f2021-03-08 14:24:46 +08002923 bmcLogEntry = {{"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Gunnar Mills743e9a12020-10-26 12:44:53 -05002924 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2925 "PostCodes/Entries/" +
2926 postcodeEntryID},
2927 {"Name", "POST Code Log Entry"},
2928 {"Id", postcodeEntryID},
2929 {"Message", std::move(msg)},
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05302930 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
Gunnar Mills743e9a12020-10-26 12:44:53 -05002931 {"MessageArgs", std::move(messageArgs)},
2932 {"EntryType", "Event"},
2933 {"Severity", std::move(severity)},
2934 {"Created", entryTimeStr}};
George Liu0ef217f2021-03-08 14:24:46 +08002935
2936 if (std::get<std::vector<uint8_t>>(code.second).size())
2937 {
2938 bmcLogEntry["AdditionalDataURI"] =
2939 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
2940 postcodeEntryID + "/attachment";
2941 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08002942 }
2943}
2944
zhanghch058d1b46d2021-04-01 11:18:24 +08002945static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002946 const uint16_t bootIndex,
2947 const uint64_t codeIndex)
2948{
2949 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302950 [aResp, bootIndex,
2951 codeIndex](const boost::system::error_code ec,
2952 const boost::container::flat_map<
2953 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2954 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002955 if (ec)
2956 {
2957 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2958 messages::internalError(aResp->res);
2959 return;
2960 }
2961
2962 // skip the empty postcode boots
2963 if (postcode.empty())
2964 {
2965 return;
2966 }
2967
2968 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2969
2970 aResp->res.jsonValue["Members@odata.count"] =
2971 aResp->res.jsonValue["Members"].size();
2972 },
Jonathan Doman15124762021-01-07 17:54:17 -08002973 "xyz.openbmc_project.State.Boot.PostCode0",
2974 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08002975 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2976 bootIndex);
2977}
2978
zhanghch058d1b46d2021-04-01 11:18:24 +08002979static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002980 const uint16_t bootIndex,
2981 const uint16_t bootCount,
2982 const uint64_t entryCount, const uint64_t skip,
2983 const uint64_t top)
2984{
2985 crow::connections::systemBus->async_method_call(
2986 [aResp, bootIndex, bootCount, entryCount, skip,
2987 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302988 const boost::container::flat_map<
2989 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2990 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002991 if (ec)
2992 {
2993 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2994 messages::internalError(aResp->res);
2995 return;
2996 }
2997
2998 uint64_t endCount = entryCount;
2999 if (!postcode.empty())
3000 {
3001 endCount = entryCount + postcode.size();
3002
3003 if ((skip < endCount) && ((top + skip) > entryCount))
3004 {
3005 uint64_t thisBootSkip =
3006 std::max(skip, entryCount) - entryCount;
3007 uint64_t thisBootTop =
3008 std::min(top + skip, endCount) - entryCount;
3009
3010 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3011 thisBootSkip, thisBootTop);
3012 }
3013 aResp->res.jsonValue["Members@odata.count"] = endCount;
3014 }
3015
3016 // continue to previous bootIndex
3017 if (bootIndex < bootCount)
3018 {
3019 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3020 bootCount, endCount, skip, top);
3021 }
3022 else
3023 {
3024 aResp->res.jsonValue["Members@odata.nextLink"] =
3025 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3026 "Entries?$skip=" +
3027 std::to_string(skip + top);
3028 }
3029 },
Jonathan Doman15124762021-01-07 17:54:17 -08003030 "xyz.openbmc_project.State.Boot.PostCode0",
3031 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003032 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3033 bootIndex);
3034}
3035
zhanghch058d1b46d2021-04-01 11:18:24 +08003036static void
3037 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3038 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003039{
3040 uint64_t entryCount = 0;
3041 crow::connections::systemBus->async_method_call(
3042 [aResp, entryCount, skip,
3043 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003044 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003045 if (ec)
3046 {
3047 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3048 messages::internalError(aResp->res);
3049 return;
3050 }
3051 auto pVal = std::get_if<uint16_t>(&bootCount);
3052 if (pVal)
3053 {
3054 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3055 }
3056 else
3057 {
3058 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3059 }
3060 },
Jonathan Doman15124762021-01-07 17:54:17 -08003061 "xyz.openbmc_project.State.Boot.PostCode0",
3062 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003063 "org.freedesktop.DBus.Properties", "Get",
3064 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3065}
3066
George Liuaf61db12021-03-08 19:36:32 +08003067inline static bool parsePostCode(std::string postCodeID, uint64_t& currentValue,
3068 uint16_t& index)
3069{
3070 // postCodeID = B1-1
3071 std::vector<std::string> split;
3072 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3073 if (split.size() != 2 || split[0].length() < 2)
3074 {
3075 return false;
3076 }
3077
3078 const char* start = split[0].data() + 1;
3079 const char* end = split[0].data() + split[0].size();
3080 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3081
3082 if (ptrIndex != end || ecIndex != std::errc() || !index)
3083 {
3084 return false;
3085 }
3086
3087 start = split[1].data();
3088 end = split[1].data() + split[1].size();
3089 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3090 if (ptrValue != end || ecValue != std::errc() || !currentValue)
3091 {
3092 return false;
3093 }
3094
3095 return true;
3096}
3097
3098inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3099{
3100 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/"
3101 "Entries/<str>/attachment/")
Gunnar Mills729f55f2021-06-30 13:44:29 -05003102 .privileges({{"Login"}})
George Liuaf61db12021-03-08 19:36:32 +08003103 .methods(boost::beast::http::verb::get)(
3104 [](const crow::Request& req,
3105 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3106 const std::string& postCodeID) {
3107 if (!http_helpers::isOctetAccepted(req))
3108 {
3109 asyncResp->res.result(
3110 boost::beast::http::status::bad_request);
3111 return;
3112 }
3113
3114 uint64_t currentValue = 0;
3115 uint16_t index = 0;
3116 if (!parsePostCode(postCodeID, currentValue, index))
3117 {
3118 messages::resourceNotFound(asyncResp->res, "LogEntry",
3119 postCodeID);
3120 return;
3121 }
3122
3123 crow::connections::systemBus->async_method_call(
3124 [asyncResp, postCodeID, currentValue](
3125 const boost::system::error_code ec,
3126 const std::vector<std::tuple<
3127 uint64_t, std::vector<uint8_t>>>& postcodes) {
3128 if (ec.value() == EBADR)
3129 {
3130 messages::resourceNotFound(asyncResp->res,
3131 "LogEntry", postCodeID);
3132 return;
3133 }
3134 if (ec)
3135 {
3136 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3137 messages::internalError(asyncResp->res);
3138 return;
3139 }
3140
3141 if (currentValue < 1 ||
3142 postcodes.size() <= currentValue)
3143 {
3144 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3145 messages::resourceNotFound(asyncResp->res,
3146 "LogEntry", postCodeID);
3147 return;
3148 }
3149
3150 size_t value = static_cast<size_t>(currentValue) - 1;
3151 auto& [tID, code] = postcodes[value];
3152
3153 if (code.size() == 0)
3154 {
3155 BMCWEB_LOG_INFO << "No found post code data";
3156 messages::internalError(asyncResp->res);
3157 return;
3158 }
3159
3160 std::string_view strData(
3161 reinterpret_cast<const char*>(code.data()),
3162 code.size());
3163
3164 asyncResp->res.addHeader("Content-Type",
3165 "application/octet-stream");
3166 asyncResp->res.addHeader("Content-Transfer-Encoding",
3167 "Base64");
3168 asyncResp->res.body() =
3169 std::move(crow::utility::base64encode(strData));
3170 },
3171 "xyz.openbmc_project.State.Boot.PostCode0",
3172 "/xyz/openbmc_project/State/Boot/PostCode0",
3173 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3174 index);
3175 });
3176}
3177
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003178inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003179{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003180 BMCWEB_ROUTE(app,
3181 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07003182 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003183 .methods(boost::beast::http::verb::get)(
3184 [](const crow::Request& req,
3185 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3186 asyncResp->res.jsonValue["@odata.type"] =
3187 "#LogEntryCollection.LogEntryCollection";
3188 asyncResp->res.jsonValue["@odata.id"] =
3189 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3190 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3191 asyncResp->res.jsonValue["Description"] =
3192 "Collection of POST Code Log Entries";
3193 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3194 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003195
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003196 uint64_t skip = 0;
3197 uint64_t top = maxEntriesPerPage; // Show max entries by default
3198 if (!getSkipParam(asyncResp, req, skip))
3199 {
3200 return;
3201 }
3202 if (!getTopParam(asyncResp, req, top))
3203 {
3204 return;
3205 }
3206 getCurrentBootNumber(asyncResp, skip, top);
3207 });
3208}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003209
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003210inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003211{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003212 BMCWEB_ROUTE(
3213 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07003214 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003215 .methods(boost::beast::http::verb::get)(
3216 [](const crow::Request&,
3217 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3218 const std::string& targetID) {
3219 size_t bootPos = targetID.find('B');
3220 if (bootPos == std::string::npos)
3221 {
3222 // Requested ID was not found
3223 messages::resourceMissingAtURI(asyncResp->res, targetID);
3224 return;
3225 }
3226 std::string_view bootIndexStr(targetID);
3227 bootIndexStr.remove_prefix(bootPos + 1);
3228 uint16_t bootIndex = 0;
3229 uint64_t codeIndex = 0;
3230 size_t dashPos = bootIndexStr.find('-');
ZhikuiRena3316fc2020-01-29 14:58:08 -08003231
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003232 if (dashPos == std::string::npos)
3233 {
3234 return;
3235 }
3236 std::string_view codeIndexStr(bootIndexStr);
3237 bootIndexStr.remove_suffix(dashPos);
3238 codeIndexStr.remove_prefix(dashPos + 1);
zhanghch058d1b46d2021-04-01 11:18:24 +08003239
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003240 bootIndex = static_cast<uint16_t>(
3241 strtoul(std::string(bootIndexStr).c_str(), nullptr, 0));
3242 codeIndex =
3243 strtoul(std::string(codeIndexStr).c_str(), nullptr, 0);
3244 if (bootIndex == 0 || codeIndex == 0)
3245 {
3246 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3247 << targetID;
3248 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003249
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003250 asyncResp->res.jsonValue["@odata.type"] =
3251 "#LogEntry.v1_4_0.LogEntry";
3252 asyncResp->res.jsonValue["@odata.id"] =
3253 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3254 "Entries";
3255 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3256 asyncResp->res.jsonValue["Description"] =
3257 "Collection of POST Code Log Entries";
3258 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3259 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003260
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003261 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3262 });
3263}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003264
Ed Tanous1da66f72018-07-27 16:13:37 -07003265} // namespace redfish