blob: 2b064146e9daa87c5a52584d3894e67324c7ad20 [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";
998 asyncResp->res.jsonValue["Entries"] = {
999 {"@odata.id",
1000 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1001 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1002
1003 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1004 "Actions/LogService.ClearLog"}};
1005 });
1006}
1007
1008inline void requestRoutesJournalEventLogClear(App& app)
1009{
1010 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1011 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001012 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001013 .methods(boost::beast::http::verb::post)(
1014 [](const crow::Request&,
1015 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1016 // Clear the EventLog by deleting the log files
1017 std::vector<std::filesystem::path> redfishLogFiles;
1018 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001019 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001020 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001021 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001022 std::error_code ec;
1023 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001024 }
1025 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001026
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001027 // Reload rsyslog so it knows to start new log files
1028 crow::connections::systemBus->async_method_call(
1029 [asyncResp](const boost::system::error_code ec) {
1030 if (ec)
1031 {
1032 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1033 << ec;
1034 messages::internalError(asyncResp->res);
1035 return;
1036 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001037
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001038 messages::success(asyncResp->res);
1039 },
1040 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1041 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1042 "rsyslog.service", "replace");
1043 });
1044}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001045
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001046static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001047 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001048 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001049{
Jason M. Bills95820182019-04-22 16:25:34 -07001050 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001051 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001052 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001053 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001054 {
1055 return 1;
1056 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001057 std::string timestamp = logEntry.substr(0, space);
1058 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001059 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001060 if (entryStart == std::string::npos)
1061 {
1062 return 1;
1063 }
1064 std::string_view entry(logEntry);
1065 entry.remove_prefix(entryStart);
1066 // Use split to separate the entry into its fields
1067 std::vector<std::string> logEntryFields;
1068 boost::split(logEntryFields, entry, boost::is_any_of(","),
1069 boost::token_compress_on);
1070 // We need at least a MessageId to be valid
1071 if (logEntryFields.size() < 1)
1072 {
1073 return 1;
1074 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001075 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001076
Jason M. Bills4851d452019-03-28 11:27:48 -07001077 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001078 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001079 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001080
Jason M. Bills4851d452019-03-28 11:27:48 -07001081 std::string msg;
1082 std::string severity;
1083 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001084 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001085 msg = message->message;
1086 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001087 }
1088
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001089 // Get the MessageArgs from the log if there are any
1090 boost::beast::span<std::string> messageArgs;
1091 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001092 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001093 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001094 // If the first string is empty, assume there are no MessageArgs
1095 std::size_t messageArgsSize = 0;
1096 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001097 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001098 messageArgsSize = logEntryFields.size() - 1;
1099 }
1100
Ed Tanous23a21a12020-07-25 04:45:05 +00001101 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001102
1103 // Fill the MessageArgs into the Message
1104 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001105 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001106 {
1107 std::string argStr = "%" + std::to_string(++i);
1108 size_t argPos = msg.find(argStr);
1109 if (argPos != std::string::npos)
1110 {
1111 msg.replace(argPos, argStr.length(), messageArg);
1112 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001113 }
1114 }
1115
Jason M. Bills95820182019-04-22 16:25:34 -07001116 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1117 // format which matches the Redfish format except for the fractional seconds
1118 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001119 std::size_t dot = timestamp.find_first_of('.');
1120 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001121 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001122 {
Jason M. Bills95820182019-04-22 16:25:34 -07001123 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001124 }
1125
1126 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001127 logEntryJson = {
George Liu0ef217f2021-03-08 14:24:46 +08001128 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001129 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001130 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001131 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001132 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001133 {"Id", logEntryID},
1134 {"Message", std::move(msg)},
1135 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001136 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001137 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001138 {"Severity", std::move(severity)},
1139 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001140 return 0;
1141}
1142
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001143inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001144{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001145 BMCWEB_ROUTE(app,
1146 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07001147 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001148 .methods(boost::beast::http::verb::get)(
1149 [](const crow::Request& req,
1150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1151 uint64_t skip = 0;
1152 uint64_t top = maxEntriesPerPage; // Show max entries by default
1153 if (!getSkipParam(asyncResp, req, skip))
Jason M. Bills95820182019-04-22 16:25:34 -07001154 {
Jason M. Bills95820182019-04-22 16:25:34 -07001155 return;
1156 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001157 if (!getTopParam(asyncResp, req, top))
Jason M. Bills897967d2019-07-29 17:05:30 -07001158 {
Jason M. Bills897967d2019-07-29 17:05:30 -07001159 return;
1160 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001161 // Collections don't include the static data added by SubRoute
1162 // because it has a duplicate entry for members
1163 asyncResp->res.jsonValue["@odata.type"] =
1164 "#LogEntryCollection.LogEntryCollection";
1165 asyncResp->res.jsonValue["@odata.id"] =
1166 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1167 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1168 asyncResp->res.jsonValue["Description"] =
1169 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001170
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001171 nlohmann::json& logEntryArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001172 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001173 logEntryArray = nlohmann::json::array();
1174 // Go through the log files and create a unique ID for each
1175 // entry
1176 std::vector<std::filesystem::path> redfishLogFiles;
1177 getRedfishLogFiles(redfishLogFiles);
1178 uint64_t entryCount = 0;
1179 std::string logEntry;
1180
1181 // Oldest logs are in the last file, so start there and loop
1182 // backwards
1183 for (auto it = redfishLogFiles.rbegin();
1184 it < redfishLogFiles.rend(); it++)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001185 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001186 std::ifstream logStream(*it);
1187 if (!logStream.is_open())
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001188 {
1189 continue;
1190 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001191
1192 // Reset the unique ID on the first entry
1193 bool firstEntry = true;
1194 while (std::getline(logStream, logEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001195 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001196 entryCount++;
1197 // Handle paging using skip (number of entries to skip
1198 // from the start) and top (number of entries to
1199 // display)
1200 if (entryCount <= skip || entryCount > skip + top)
George Liuebd45902020-08-26 14:21:10 +08001201 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001202 continue;
George Liuebd45902020-08-26 14:21:10 +08001203 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001204
1205 std::string idStr;
1206 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
George Liuebd45902020-08-26 14:21:10 +08001207 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001208 continue;
George Liuebd45902020-08-26 14:21:10 +08001209 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001210
1211 if (firstEntry)
1212 {
1213 firstEntry = false;
1214 }
1215
1216 logEntryArray.push_back({});
1217 nlohmann::json& bmcLogEntry = logEntryArray.back();
1218 if (fillEventLogEntryJson(idStr, logEntry,
1219 bmcLogEntry) != 0)
Xiaochao Ma75710de2021-01-21 17:56:02 +08001220 {
1221 messages::internalError(asyncResp->res);
1222 return;
1223 }
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001224 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001225 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001226 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1227 if (skip + top < entryCount)
Ed Tanous271584a2019-07-09 16:24:22 -07001228 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001229 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001230 "/redfish/v1/Systems/system/LogServices/EventLog/"
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001231 "Entries?$skip=" +
1232 std::to_string(skip + top);
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001233 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001234 });
1235}
Chicago Duan336e96c2019-07-15 14:22:08 +08001236
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001237inline void requestRoutesJournalEventLogEntry(App& app)
1238{
1239 BMCWEB_ROUTE(
1240 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001241 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001242 .methods(boost::beast::http::verb::get)(
1243 [](const crow::Request&,
1244 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1245 const std::string& param) {
1246 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001247
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001248 // Go through the log files and check the unique ID for each
1249 // entry to find the target entry
1250 std::vector<std::filesystem::path> redfishLogFiles;
1251 getRedfishLogFiles(redfishLogFiles);
1252 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001253
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001254 // Oldest logs are in the last file, so start there and loop
1255 // backwards
1256 for (auto it = redfishLogFiles.rbegin();
1257 it < redfishLogFiles.rend(); it++)
1258 {
1259 std::ifstream logStream(*it);
1260 if (!logStream.is_open())
1261 {
1262 continue;
1263 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001264
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001265 // Reset the unique ID on the first entry
1266 bool firstEntry = true;
1267 while (std::getline(logStream, logEntry))
1268 {
1269 std::string idStr;
1270 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1271 {
1272 continue;
1273 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001274
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001275 if (firstEntry)
1276 {
1277 firstEntry = false;
1278 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001279
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001280 if (idStr == targetID)
1281 {
1282 if (fillEventLogEntryJson(
1283 idStr, logEntry,
1284 asyncResp->res.jsonValue) != 0)
1285 {
1286 messages::internalError(asyncResp->res);
1287 return;
1288 }
1289 return;
1290 }
1291 }
1292 }
1293 // Requested ID was not found
1294 messages::resourceMissingAtURI(asyncResp->res, targetID);
1295 });
1296}
1297
1298inline void requestRoutesDBusEventLogEntryCollection(App& app)
1299{
1300 BMCWEB_ROUTE(app,
1301 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07001302 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001303 .methods(
1304 boost::beast::http::verb::
1305 get)([](const crow::Request&,
1306 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1307 // Collections don't include the static data added by SubRoute
1308 // because it has a duplicate entry for members
1309 asyncResp->res.jsonValue["@odata.type"] =
1310 "#LogEntryCollection.LogEntryCollection";
1311 asyncResp->res.jsonValue["@odata.id"] =
1312 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1313 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1314 asyncResp->res.jsonValue["Description"] =
1315 "Collection of System Event Log Entries";
1316
1317 // DBus implementation of EventLog/Entries
1318 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001319 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001320 [asyncResp](const boost::system::error_code ec,
1321 GetManagedObjectsType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001322 if (ec)
1323 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001324 // TODO Handle for specific error code
1325 BMCWEB_LOG_ERROR
1326 << "getLogEntriesIfaceData resp_handler got error "
1327 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001328 messages::internalError(asyncResp->res);
1329 return;
1330 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001331 nlohmann::json& entriesArray =
1332 asyncResp->res.jsonValue["Members"];
1333 entriesArray = nlohmann::json::array();
1334 for (auto& objectPath : resp)
1335 {
1336 uint32_t* id = nullptr;
1337 std::time_t timestamp{};
1338 std::time_t updateTimestamp{};
1339 std::string* severity = nullptr;
1340 std::string* message = nullptr;
1341 std::string* filePath = nullptr;
1342 bool resolved = false;
1343 for (auto& interfaceMap : objectPath.second)
1344 {
1345 if (interfaceMap.first ==
1346 "xyz.openbmc_project.Logging.Entry")
1347 {
1348 for (auto& propertyMap : interfaceMap.second)
1349 {
1350 if (propertyMap.first == "Id")
1351 {
1352 id = std::get_if<uint32_t>(
1353 &propertyMap.second);
1354 }
1355 else if (propertyMap.first == "Timestamp")
1356 {
1357 const uint64_t* millisTimeStamp =
1358 std::get_if<uint64_t>(
1359 &propertyMap.second);
1360 if (millisTimeStamp != nullptr)
1361 {
1362 timestamp =
1363 crow::utility::getTimestamp(
1364 *millisTimeStamp);
1365 }
1366 }
1367 else if (propertyMap.first ==
1368 "UpdateTimestamp")
1369 {
1370 const uint64_t* millisTimeStamp =
1371 std::get_if<uint64_t>(
1372 &propertyMap.second);
1373 if (millisTimeStamp != nullptr)
1374 {
1375 updateTimestamp =
1376 crow::utility::getTimestamp(
1377 *millisTimeStamp);
1378 }
1379 }
1380 else if (propertyMap.first == "Severity")
1381 {
1382 severity = std::get_if<std::string>(
1383 &propertyMap.second);
1384 }
1385 else if (propertyMap.first == "Message")
1386 {
1387 message = std::get_if<std::string>(
1388 &propertyMap.second);
1389 }
1390 else if (propertyMap.first == "Resolved")
1391 {
1392 bool* resolveptr = std::get_if<bool>(
1393 &propertyMap.second);
1394 if (resolveptr == nullptr)
1395 {
1396 messages::internalError(
1397 asyncResp->res);
1398 return;
1399 }
1400 resolved = *resolveptr;
1401 }
1402 }
1403 if (id == nullptr || message == nullptr ||
1404 severity == nullptr)
1405 {
1406 messages::internalError(asyncResp->res);
1407 return;
1408 }
1409 }
1410 else if (interfaceMap.first ==
1411 "xyz.openbmc_project.Common.FilePath")
1412 {
1413 for (auto& propertyMap : interfaceMap.second)
1414 {
1415 if (propertyMap.first == "Path")
1416 {
1417 filePath = std::get_if<std::string>(
1418 &propertyMap.second);
1419 }
1420 }
1421 }
1422 }
1423 // Object path without the
1424 // xyz.openbmc_project.Logging.Entry interface, ignore
1425 // and continue.
1426 if (id == nullptr || message == nullptr ||
1427 severity == nullptr)
1428 {
1429 continue;
1430 }
1431 entriesArray.push_back({});
1432 nlohmann::json& thisEntry = entriesArray.back();
1433 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1434 thisEntry["@odata.id"] =
1435 "/redfish/v1/Systems/system/"
1436 "LogServices/EventLog/Entries/" +
1437 std::to_string(*id);
1438 thisEntry["Name"] = "System Event Log Entry";
1439 thisEntry["Id"] = std::to_string(*id);
1440 thisEntry["Message"] = *message;
1441 thisEntry["Resolved"] = resolved;
1442 thisEntry["EntryType"] = "Event";
1443 thisEntry["Severity"] =
1444 translateSeverityDbusToRedfish(*severity);
1445 thisEntry["Created"] =
1446 crow::utility::getDateTime(timestamp);
1447 thisEntry["Modified"] =
1448 crow::utility::getDateTime(updateTimestamp);
1449 if (filePath != nullptr)
1450 {
1451 thisEntry["AdditionalDataURI"] =
1452 "/redfish/v1/Systems/system/LogServices/"
1453 "EventLog/"
1454 "Entries/" +
1455 std::to_string(*id) + "/attachment";
1456 }
1457 }
1458 std::sort(entriesArray.begin(), entriesArray.end(),
1459 [](const nlohmann::json& left,
1460 const nlohmann::json& right) {
1461 return (left["Id"] <= right["Id"]);
1462 });
1463 asyncResp->res.jsonValue["Members@odata.count"] =
1464 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001465 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001466 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1467 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1468 });
1469}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001470
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001471inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001472{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001473 BMCWEB_ROUTE(
1474 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001475 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001476 .methods(boost::beast::http::verb::get)(
1477 [](const crow::Request&,
1478 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1479 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001480
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001481 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001482 std::string entryID = param;
1483 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001484
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001485 // DBus implementation of EventLog/Entries
1486 // Make call to Logging Service to find all log entry objects
1487 crow::connections::systemBus->async_method_call(
1488 [asyncResp, entryID](const boost::system::error_code ec,
1489 GetManagedPropertyType& resp) {
1490 if (ec.value() == EBADR)
1491 {
1492 messages::resourceNotFound(
1493 asyncResp->res, "EventLogEntry", entryID);
1494 return;
1495 }
1496 if (ec)
1497 {
1498 BMCWEB_LOG_ERROR << "EventLogEntry (DBus) "
1499 "resp_handler got error "
1500 << ec;
1501 messages::internalError(asyncResp->res);
1502 return;
1503 }
1504 uint32_t* id = nullptr;
1505 std::time_t timestamp{};
1506 std::time_t updateTimestamp{};
1507 std::string* severity = nullptr;
1508 std::string* message = nullptr;
1509 std::string* filePath = nullptr;
1510 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001511
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001512 for (auto& propertyMap : resp)
1513 {
1514 if (propertyMap.first == "Id")
1515 {
1516 id = std::get_if<uint32_t>(&propertyMap.second);
1517 }
1518 else if (propertyMap.first == "Timestamp")
1519 {
1520 const uint64_t* millisTimeStamp =
1521 std::get_if<uint64_t>(&propertyMap.second);
1522 if (millisTimeStamp != nullptr)
1523 {
1524 timestamp = crow::utility::getTimestamp(
1525 *millisTimeStamp);
1526 }
1527 }
1528 else if (propertyMap.first == "UpdateTimestamp")
1529 {
1530 const uint64_t* millisTimeStamp =
1531 std::get_if<uint64_t>(&propertyMap.second);
1532 if (millisTimeStamp != nullptr)
1533 {
1534 updateTimestamp =
1535 crow::utility::getTimestamp(
1536 *millisTimeStamp);
1537 }
1538 }
1539 else if (propertyMap.first == "Severity")
1540 {
1541 severity = std::get_if<std::string>(
1542 &propertyMap.second);
1543 }
1544 else if (propertyMap.first == "Message")
1545 {
1546 message = std::get_if<std::string>(
1547 &propertyMap.second);
1548 }
1549 else if (propertyMap.first == "Resolved")
1550 {
1551 bool* resolveptr =
1552 std::get_if<bool>(&propertyMap.second);
1553 if (resolveptr == nullptr)
1554 {
1555 messages::internalError(asyncResp->res);
1556 return;
1557 }
1558 resolved = *resolveptr;
1559 }
1560 else if (propertyMap.first == "Path")
1561 {
1562 filePath = std::get_if<std::string>(
1563 &propertyMap.second);
1564 }
1565 }
1566 if (id == nullptr || message == nullptr ||
1567 severity == nullptr)
1568 {
1569 messages::internalError(asyncResp->res);
1570 return;
1571 }
1572 asyncResp->res.jsonValue["@odata.type"] =
1573 "#LogEntry.v1_8_0.LogEntry";
1574 asyncResp->res.jsonValue["@odata.id"] =
1575 "/redfish/v1/Systems/system/LogServices/EventLog/"
1576 "Entries/" +
1577 std::to_string(*id);
1578 asyncResp->res.jsonValue["Name"] =
1579 "System Event Log Entry";
1580 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1581 asyncResp->res.jsonValue["Message"] = *message;
1582 asyncResp->res.jsonValue["Resolved"] = resolved;
1583 asyncResp->res.jsonValue["EntryType"] = "Event";
1584 asyncResp->res.jsonValue["Severity"] =
1585 translateSeverityDbusToRedfish(*severity);
1586 asyncResp->res.jsonValue["Created"] =
1587 crow::utility::getDateTime(timestamp);
1588 asyncResp->res.jsonValue["Modified"] =
1589 crow::utility::getDateTime(updateTimestamp);
1590 if (filePath != nullptr)
1591 {
1592 asyncResp->res.jsonValue["AdditionalDataURI"] =
1593 "/redfish/v1/Systems/system/LogServices/"
1594 "EventLog/"
1595 "attachment/" +
1596 std::to_string(*id);
1597 }
1598 },
1599 "xyz.openbmc_project.Logging",
1600 "/xyz/openbmc_project/logging/entry/" + entryID,
1601 "org.freedesktop.DBus.Properties", "GetAll", "");
1602 });
1603
1604 BMCWEB_ROUTE(
1605 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001606 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001607 .methods(boost::beast::http::verb::patch)(
1608 [](const crow::Request& req,
1609 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1610 const std::string& entryId) {
1611 std::optional<bool> resolved;
1612
1613 if (!json_util::readJson(req, asyncResp->res, "Resolved",
1614 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001615 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001616 return;
1617 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001618 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001619
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001620 crow::connections::systemBus->async_method_call(
1621 [asyncResp, resolved,
1622 entryId](const boost::system::error_code ec) {
1623 if (ec)
1624 {
1625 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1626 messages::internalError(asyncResp->res);
1627 return;
1628 }
1629 },
1630 "xyz.openbmc_project.Logging",
1631 "/xyz/openbmc_project/logging/entry/" + entryId,
1632 "org.freedesktop.DBus.Properties", "Set",
1633 "xyz.openbmc_project.Logging.Entry", "Resolved",
1634 std::variant<bool>(*resolved));
1635 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001636
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001637 BMCWEB_ROUTE(
1638 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001639 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001640 .methods(boost::beast::http::verb::delete_)(
1641 [](const crow::Request&,
1642 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1643 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001644
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001645 {
1646 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001647
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001648 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001649
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001650 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001651
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001652 // Process response from Logging service.
1653 auto respHandler = [asyncResp, entryID](
1654 const boost::system::error_code ec) {
1655 BMCWEB_LOG_DEBUG
1656 << "EventLogEntry (DBus) doDelete callback: Done";
1657 if (ec)
1658 {
1659 if (ec.value() == EBADR)
1660 {
1661 messages::resourceNotFound(asyncResp->res,
1662 "LogEntry", entryID);
1663 return;
1664 }
1665 // TODO Handle for specific error code
1666 BMCWEB_LOG_ERROR << "EventLogEntry (DBus) doDelete "
1667 "respHandler got error "
1668 << ec;
1669 asyncResp->res.result(
1670 boost::beast::http::status::internal_server_error);
1671 return;
1672 }
1673
1674 asyncResp->res.result(boost::beast::http::status::ok);
1675 };
1676
1677 // Make call to Logging service to request Delete Log
1678 crow::connections::systemBus->async_method_call(
1679 respHandler, "xyz.openbmc_project.Logging",
1680 "/xyz/openbmc_project/logging/entry/" + entryID,
1681 "xyz.openbmc_project.Object.Delete", "Delete");
1682 });
1683}
1684
1685inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001686{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001687 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/"
1688 "<str>/attachment")
Ed Tanous432a8902021-06-14 15:28:56 -07001689 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001690 .methods(boost::beast::http::verb::get)(
1691 [](const crow::Request& req,
1692 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1693 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001694
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001695 {
George Liuaf61db12021-03-08 19:36:32 +08001696 if (!http_helpers::isOctetAccepted(req))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001697 {
1698 asyncResp->res.result(
1699 boost::beast::http::status::bad_request);
1700 return;
1701 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001702
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001703 std::string entryID = param;
1704 dbus::utility::escapePathForDbus(entryID);
1705
1706 crow::connections::systemBus->async_method_call(
1707 [asyncResp,
1708 entryID](const boost::system::error_code ec,
1709 const sdbusplus::message::unix_fd& unixfd) {
1710 if (ec.value() == EBADR)
1711 {
1712 messages::resourceNotFound(
1713 asyncResp->res, "EventLogAttachment", entryID);
1714 return;
1715 }
1716 if (ec)
1717 {
1718 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1719 messages::internalError(asyncResp->res);
1720 return;
1721 }
1722
1723 int fd = -1;
1724 fd = dup(unixfd);
1725 if (fd == -1)
1726 {
1727 messages::internalError(asyncResp->res);
1728 return;
1729 }
1730
1731 long long int size = lseek(fd, 0, SEEK_END);
1732 if (size == -1)
1733 {
1734 messages::internalError(asyncResp->res);
1735 return;
1736 }
1737
1738 // Arbitrary max size of 64kb
1739 constexpr int maxFileSize = 65536;
1740 if (size > maxFileSize)
1741 {
1742 BMCWEB_LOG_ERROR
1743 << "File size exceeds maximum allowed size of "
1744 << maxFileSize;
1745 messages::internalError(asyncResp->res);
1746 return;
1747 }
1748 std::vector<char> data(static_cast<size_t>(size));
1749 long long int rc = lseek(fd, 0, SEEK_SET);
1750 if (rc == -1)
1751 {
1752 messages::internalError(asyncResp->res);
1753 return;
1754 }
1755 rc = read(fd, data.data(), data.size());
1756 if ((rc == -1) || (rc != size))
1757 {
1758 messages::internalError(asyncResp->res);
1759 return;
1760 }
1761 close(fd);
1762
1763 std::string_view strData(data.data(), data.size());
1764 std::string output =
1765 crow::utility::base64encode(strData);
1766
1767 asyncResp->res.addHeader("Content-Type",
1768 "application/octet-stream");
1769 asyncResp->res.addHeader("Content-Transfer-Encoding",
1770 "Base64");
1771 asyncResp->res.body() = std::move(output);
1772 },
1773 "xyz.openbmc_project.Logging",
1774 "/xyz/openbmc_project/logging/entry/" + entryID,
1775 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1776 });
1777}
1778
1779inline void requestRoutesBMCLogServiceCollection(App& app)
1780{
1781 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous432a8902021-06-14 15:28:56 -07001782 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001783 .methods(boost::beast::http::verb::get)(
1784 [](const crow::Request&,
1785 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1786 // Collections don't include the static data added by SubRoute
1787 // because it has a duplicate entry for members
1788 asyncResp->res.jsonValue["@odata.type"] =
1789 "#LogServiceCollection.LogServiceCollection";
1790 asyncResp->res.jsonValue["@odata.id"] =
1791 "/redfish/v1/Managers/bmc/LogServices";
1792 asyncResp->res.jsonValue["Name"] =
1793 "Open BMC Log Services Collection";
1794 asyncResp->res.jsonValue["Description"] =
1795 "Collection of LogServices for this Manager";
1796 nlohmann::json& logServiceArray =
1797 asyncResp->res.jsonValue["Members"];
1798 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001799#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001800 logServiceArray.push_back(
1801 {{"@odata.id",
1802 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001803#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001804#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001805 logServiceArray.push_back(
1806 {{"@odata.id",
1807 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001808#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001809 asyncResp->res.jsonValue["Members@odata.count"] =
1810 logServiceArray.size();
1811 });
1812}
Ed Tanous1da66f72018-07-27 16:13:37 -07001813
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001814inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07001815{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001816 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanous432a8902021-06-14 15:28:56 -07001817 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001818 .methods(boost::beast::http::verb::get)(
1819 [](const crow::Request&,
1820 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07001821
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001822 {
1823 asyncResp->res.jsonValue["@odata.type"] =
1824 "#LogService.v1_1_0.LogService";
1825 asyncResp->res.jsonValue["@odata.id"] =
1826 "/redfish/v1/Managers/bmc/LogServices/Journal";
1827 asyncResp->res.jsonValue["Name"] =
1828 "Open BMC Journal Log Service";
1829 asyncResp->res.jsonValue["Description"] =
1830 "BMC Journal Log Service";
1831 asyncResp->res.jsonValue["Id"] = "BMC Journal";
1832 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1833 asyncResp->res.jsonValue["Entries"] = {
1834 {"@odata.id",
1835 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
1836 });
1837}
Jason M. Billse1f26342018-07-18 12:12:00 -07001838
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001839static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1840 sd_journal* journal,
1841 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001842{
1843 // Get the Log Entry contents
1844 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001845
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001846 std::string message;
1847 std::string_view syslogID;
1848 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
1849 if (ret < 0)
1850 {
1851 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
1852 << strerror(-ret);
1853 }
1854 if (!syslogID.empty())
1855 {
1856 message += std::string(syslogID) + ": ";
1857 }
1858
Ed Tanous39e77502019-03-04 17:35:53 -08001859 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001860 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001861 if (ret < 0)
1862 {
1863 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1864 return 1;
1865 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001866 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001867
1868 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001869 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001870 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001871 if (ret < 0)
1872 {
1873 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001874 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001875
1876 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001877 std::string entryTimeStr;
1878 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001879 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001880 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001881 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001882
1883 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001884 bmcJournalLogEntryJson = {
George Liu0ef217f2021-03-08 14:24:46 +08001885 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001886 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1887 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001888 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001889 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001890 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07001891 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06001892 {"Severity", severity <= 2 ? "Critical"
1893 : severity <= 4 ? "Warning"
1894 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001895 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001896 {"Created", std::move(entryTimeStr)}};
1897 return 0;
1898}
1899
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001900inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07001901{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001902 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07001903 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001904 .methods(boost::beast::http::verb::get)(
1905 [](const crow::Request& req,
1906 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1907 static constexpr const long maxEntriesPerPage = 1000;
1908 uint64_t skip = 0;
1909 uint64_t top = maxEntriesPerPage; // Show max entries by default
1910 if (!getSkipParam(asyncResp, req, skip))
1911 {
1912 return;
1913 }
1914 if (!getTopParam(asyncResp, req, top))
1915 {
1916 return;
1917 }
1918 // Collections don't include the static data added by SubRoute
1919 // because it has a duplicate entry for members
1920 asyncResp->res.jsonValue["@odata.type"] =
1921 "#LogEntryCollection.LogEntryCollection";
1922 asyncResp->res.jsonValue["@odata.id"] =
1923 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1924 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1925 asyncResp->res.jsonValue["Description"] =
1926 "Collection of BMC Journal Entries";
1927 nlohmann::json& logEntryArray =
1928 asyncResp->res.jsonValue["Members"];
1929 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07001930
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001931 // Go through the journal and use the timestamp to create a
1932 // unique ID for each entry
1933 sd_journal* journalTmp = nullptr;
1934 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1935 if (ret < 0)
1936 {
1937 BMCWEB_LOG_ERROR << "failed to open journal: "
1938 << strerror(-ret);
1939 messages::internalError(asyncResp->res);
1940 return;
1941 }
1942 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
1943 journal(journalTmp, sd_journal_close);
1944 journalTmp = nullptr;
1945 uint64_t entryCount = 0;
1946 // Reset the unique ID on the first entry
1947 bool firstEntry = true;
1948 SD_JOURNAL_FOREACH(journal.get())
1949 {
1950 entryCount++;
1951 // Handle paging using skip (number of entries to skip from
1952 // the start) and top (number of entries to display)
1953 if (entryCount <= skip || entryCount > skip + top)
1954 {
1955 continue;
1956 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001957
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001958 std::string idStr;
1959 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1960 {
1961 continue;
1962 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001963
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001964 if (firstEntry)
1965 {
1966 firstEntry = false;
1967 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001968
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001969 logEntryArray.push_back({});
1970 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
1971 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1972 bmcJournalLogEntry) != 0)
1973 {
1974 messages::internalError(asyncResp->res);
1975 return;
1976 }
1977 }
1978 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1979 if (skip + top < entryCount)
1980 {
1981 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1982 "/redfish/v1/Managers/bmc/LogServices/Journal/"
1983 "Entries?$skip=" +
1984 std::to_string(skip + top);
1985 }
1986 });
1987}
Jason M. Billse1f26342018-07-18 12:12:00 -07001988
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001989inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07001990{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001991 BMCWEB_ROUTE(app,
1992 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07001993 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001994 .methods(boost::beast::http::verb::get)(
1995 [](const crow::Request&,
1996 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1997 const std::string& entryID) {
1998 // Convert the unique ID back to a timestamp to find the entry
1999 uint64_t ts = 0;
2000 uint64_t index = 0;
2001 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2002 {
2003 return;
2004 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002005
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002006 sd_journal* journalTmp = nullptr;
2007 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2008 if (ret < 0)
2009 {
2010 BMCWEB_LOG_ERROR << "failed to open journal: "
2011 << strerror(-ret);
2012 messages::internalError(asyncResp->res);
2013 return;
2014 }
2015 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2016 journal(journalTmp, sd_journal_close);
2017 journalTmp = nullptr;
2018 // Go to the timestamp in the log and move to the entry at the
2019 // index tracking the unique ID
2020 std::string idStr;
2021 bool firstEntry = true;
2022 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2023 if (ret < 0)
2024 {
2025 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2026 << strerror(-ret);
2027 messages::internalError(asyncResp->res);
2028 return;
2029 }
2030 for (uint64_t i = 0; i <= index; i++)
2031 {
2032 sd_journal_next(journal.get());
2033 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2034 {
2035 messages::internalError(asyncResp->res);
2036 return;
2037 }
2038 if (firstEntry)
2039 {
2040 firstEntry = false;
2041 }
2042 }
2043 // Confirm that the entry ID matches what was requested
2044 if (idStr != entryID)
2045 {
2046 messages::resourceMissingAtURI(asyncResp->res, entryID);
2047 return;
2048 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002049
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002050 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2051 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002052 {
2053 messages::internalError(asyncResp->res);
2054 return;
2055 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002056 });
2057}
2058
2059inline void requestRoutesBMCDumpService(App& app)
2060{
2061 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanous432a8902021-06-14 15:28:56 -07002062 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002063 .methods(boost::beast::http::verb::get)(
2064 [](const crow::Request&,
2065 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2066 asyncResp->res.jsonValue["@odata.id"] =
2067 "/redfish/v1/Managers/bmc/LogServices/Dump";
2068 asyncResp->res.jsonValue["@odata.type"] =
2069 "#LogService.v1_2_0.LogService";
2070 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2071 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2072 asyncResp->res.jsonValue["Id"] = "Dump";
2073 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2074 asyncResp->res.jsonValue["Entries"] = {
2075 {"@odata.id",
2076 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2077 asyncResp->res.jsonValue["Actions"] = {
2078 {"#LogService.ClearLog",
2079 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2080 "Actions/LogService.ClearLog"}}},
2081 {"#LogService.CollectDiagnosticData",
2082 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2083 "Actions/LogService.CollectDiagnosticData"}}}};
2084 });
2085}
2086
2087inline void requestRoutesBMCDumpEntryCollection(App& app)
2088{
2089
2090 /**
2091 * Functions triggers appropriate requests on DBus
2092 */
2093 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07002094 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002095 .methods(boost::beast::http::verb::get)(
2096 [](const crow::Request&,
2097 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2098 asyncResp->res.jsonValue["@odata.type"] =
2099 "#LogEntryCollection.LogEntryCollection";
2100 asyncResp->res.jsonValue["@odata.id"] =
2101 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2102 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2103 asyncResp->res.jsonValue["Description"] =
2104 "Collection of BMC Dump Entries";
2105
2106 getDumpEntryCollection(asyncResp, "BMC");
2107 });
2108}
2109
2110inline void requestRoutesBMCDumpEntry(App& app)
2111{
2112 BMCWEB_ROUTE(app,
2113 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002114 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002115 .methods(boost::beast::http::verb::get)(
2116 [](const crow::Request&,
2117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2118 const std::string& param) {
2119 getDumpEntryById(asyncResp, param, "BMC");
2120 });
2121 BMCWEB_ROUTE(app,
2122 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002123 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002124 .methods(boost::beast::http::verb::delete_)(
2125 [](const crow::Request&,
2126 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2127 const std::string& param) {
2128 deleteDumpEntry(asyncResp, param, "bmc");
2129 });
2130}
2131
2132inline void requestRoutesBMCDumpCreate(App& app)
2133{
2134
2135 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2136 "Actions/"
2137 "LogService.CollectDiagnosticData/")
Ed Tanous432a8902021-06-14 15:28:56 -07002138 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002139 .methods(boost::beast::http::verb::post)(
2140 [](const crow::Request& req,
2141 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2142 createDump(asyncResp, req, "BMC");
2143 });
2144}
2145
2146inline void requestRoutesBMCDumpClear(App& app)
2147{
2148 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2149 "Actions/"
2150 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002151 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002152 .methods(boost::beast::http::verb::post)(
2153 [](const crow::Request&,
2154 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2155 clearDump(asyncResp, "BMC");
2156 });
2157}
2158
2159inline void requestRoutesSystemDumpService(App& app)
2160{
2161 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanous432a8902021-06-14 15:28:56 -07002162 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002163 .methods(boost::beast::http::verb::get)(
2164 [](const crow::Request&,
2165 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2166
2167 {
2168 asyncResp->res.jsonValue["@odata.id"] =
2169 "/redfish/v1/Systems/system/LogServices/Dump";
2170 asyncResp->res.jsonValue["@odata.type"] =
2171 "#LogService.v1_2_0.LogService";
2172 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2173 asyncResp->res.jsonValue["Description"] =
2174 "System Dump LogService";
2175 asyncResp->res.jsonValue["Id"] = "Dump";
2176 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2177 asyncResp->res.jsonValue["Entries"] = {
2178 {"@odata.id",
2179 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2180 asyncResp->res.jsonValue["Actions"] = {
2181 {"#LogService.ClearLog",
2182 {{"target",
2183 "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2184 "LogService.ClearLog"}}},
2185 {"#LogService.CollectDiagnosticData",
2186 {{"target",
2187 "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2188 "LogService.CollectDiagnosticData"}}}};
2189 });
2190}
2191
2192inline void requestRoutesSystemDumpEntryCollection(App& app)
2193{
2194
2195 /**
2196 * Functions triggers appropriate requests on DBus
2197 */
Charles Boyerf9a67082021-06-15 08:29:29 -05002198 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07002199 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002200 .methods(boost::beast::http::verb::get)(
2201 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002202 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002203 asyncResp->res.jsonValue["@odata.type"] =
2204 "#LogEntryCollection.LogEntryCollection";
2205 asyncResp->res.jsonValue["@odata.id"] =
2206 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2207 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2208 asyncResp->res.jsonValue["Description"] =
2209 "Collection of System Dump Entries";
2210
2211 getDumpEntryCollection(asyncResp, "System");
2212 });
2213}
2214
2215inline void requestRoutesSystemDumpEntry(App& app)
2216{
2217 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002218 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002219 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002220 .methods(boost::beast::http::verb::get)(
2221 [](const crow::Request&,
2222 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2223 const std::string& param) {
2224 getDumpEntryById(asyncResp, param, "System");
2225 });
2226
2227 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002228 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002229 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002230 .methods(boost::beast::http::verb::delete_)(
2231 [](const crow::Request&,
2232 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2233 const std::string& param) {
2234 deleteDumpEntry(asyncResp, param, "system");
2235 });
2236}
2237
2238inline void requestRoutesSystemDumpCreate(App& app)
2239{
2240 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2241 "Actions/"
2242 "LogService.CollectDiagnosticData/")
Ed Tanous432a8902021-06-14 15:28:56 -07002243 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002244 .methods(boost::beast::http::verb::post)(
2245 [](const crow::Request& req,
2246 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2247
2248 { createDump(asyncResp, req, "System"); });
2249}
2250
2251inline void requestRoutesSystemDumpClear(App& app)
2252{
2253 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2254 "Actions/"
2255 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002256 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002257 .methods(boost::beast::http::verb::post)(
2258 [](const crow::Request&,
2259 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2260
2261 { clearDump(asyncResp, "System"); });
2262}
2263
2264inline void requestRoutesCrashdumpService(App& app)
2265{
2266 // Note: Deviated from redfish privilege registry for GET & HEAD
2267 // method for security reasons.
2268 /**
2269 * Functions triggers appropriate requests on DBus
2270 */
2271 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous432a8902021-06-14 15:28:56 -07002272 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002273 .methods(
2274 boost::beast::http::verb::
2275 get)([](const crow::Request&,
2276 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2277 // Copy over the static data to include the entries added by
2278 // SubRoute
2279 asyncResp->res.jsonValue["@odata.id"] =
2280 "/redfish/v1/Systems/system/LogServices/Crashdump";
2281 asyncResp->res.jsonValue["@odata.type"] =
2282 "#LogService.v1_2_0.LogService";
2283 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2284 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2285 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2286 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2287 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
2288 asyncResp->res.jsonValue["Entries"] = {
2289 {"@odata.id",
2290 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2291 asyncResp->res.jsonValue["Actions"] = {
2292 {"#LogService.ClearLog",
2293 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2294 "Actions/LogService.ClearLog"}}},
2295 {"#LogService.CollectDiagnosticData",
2296 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2297 "Actions/LogService.CollectDiagnosticData"}}}};
2298 });
2299}
2300
2301void inline requestRoutesCrashdumpClear(App& app)
2302{
2303 BMCWEB_ROUTE(app,
2304 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2305 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002306 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002307 .methods(boost::beast::http::verb::post)(
2308 [](const crow::Request&,
2309 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2310 crow::connections::systemBus->async_method_call(
2311 [asyncResp](const boost::system::error_code ec,
2312 const std::string&) {
2313 if (ec)
2314 {
2315 messages::internalError(asyncResp->res);
2316 return;
2317 }
2318 messages::success(asyncResp->res);
2319 },
2320 crashdumpObject, crashdumpPath, deleteAllInterface,
2321 "DeleteAll");
2322 });
2323}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002324
zhanghch058d1b46d2021-04-01 11:18:24 +08002325static void
2326 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2327 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002328{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002329 auto getStoredLogCallback =
2330 [asyncResp, logID, &logEntryJson](
2331 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002332 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002333 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002334 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002335 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2336 if (ec.value() ==
2337 boost::system::linux_error::bad_request_descriptor)
2338 {
2339 messages::resourceNotFound(asyncResp->res, "LogEntry",
2340 logID);
2341 }
2342 else
2343 {
2344 messages::internalError(asyncResp->res);
2345 }
2346 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002347 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002348
Johnathan Mantey043a0532020-03-10 17:15:28 -07002349 std::string timestamp{};
2350 std::string filename{};
2351 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002352 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002353
2354 if (filename.empty() || timestamp.empty())
2355 {
2356 messages::resourceMissingAtURI(asyncResp->res, logID);
2357 return;
2358 }
2359
2360 std::string crashdumpURI =
2361 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2362 logID + "/" + filename;
George Liu0ef217f2021-03-08 14:24:46 +08002363 logEntryJson = {{"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002364 {"@odata.id", "/redfish/v1/Systems/system/"
2365 "LogServices/Crashdump/Entries/" +
2366 logID},
2367 {"Name", "CPU Crashdump"},
2368 {"Id", logID},
2369 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002370 {"AdditionalDataURI", std::move(crashdumpURI)},
2371 {"DiagnosticDataType", "OEM"},
2372 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002373 {"Created", std::move(timestamp)}};
2374 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002375 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002376 std::move(getStoredLogCallback), crashdumpObject,
2377 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002378 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002379}
2380
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002381inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002382{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002383 // Note: Deviated from redfish privilege registry for GET & HEAD
2384 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002385 /**
2386 * Functions triggers appropriate requests on DBus
2387 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002388 BMCWEB_ROUTE(app,
2389 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07002390 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002391 .methods(
2392 boost::beast::http::verb::
2393 get)([](const crow::Request&,
2394 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2395 // Collections don't include the static data added by SubRoute
2396 // because it has a duplicate entry for members
2397 auto getLogEntriesCallback = [asyncResp](
2398 const boost::system::error_code ec,
2399 const std::vector<std::string>&
2400 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002401 if (ec)
2402 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002403 if (ec.value() !=
2404 boost::system::errc::no_such_file_or_directory)
2405 {
2406 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2407 << ec.message();
2408 messages::internalError(asyncResp->res);
2409 return;
2410 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002411 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002412 asyncResp->res.jsonValue["@odata.type"] =
2413 "#LogEntryCollection.LogEntryCollection";
2414 asyncResp->res.jsonValue["@odata.id"] =
2415 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2416 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2417 asyncResp->res.jsonValue["Description"] =
2418 "Collection of Crashdump Entries";
2419 nlohmann::json& logEntryArray =
2420 asyncResp->res.jsonValue["Members"];
2421 logEntryArray = nlohmann::json::array();
2422 std::vector<std::string> logIDs;
2423 // Get the list of log entries and build up an empty array big
2424 // enough to hold them
2425 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002426 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002427 // Get the log ID
2428 std::size_t lastPos = objpath.rfind('/');
2429 if (lastPos == std::string::npos)
2430 {
2431 continue;
2432 }
2433 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002434
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002435 // Add a space for the log entry to the array
2436 logEntryArray.push_back({});
2437 }
2438 // Now go through and set up async calls to fill in the entries
2439 size_t index = 0;
2440 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002441 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002442 // Add the log entry to the array
2443 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002444 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002445 asyncResp->res.jsonValue["Members@odata.count"] =
2446 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002447 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002448 crow::connections::systemBus->async_method_call(
2449 std::move(getLogEntriesCallback),
2450 "xyz.openbmc_project.ObjectMapper",
2451 "/xyz/openbmc_project/object_mapper",
2452 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2453 std::array<const char*, 1>{crashdumpInterface});
2454 });
2455}
Ed Tanous1da66f72018-07-27 16:13:37 -07002456
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002457inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002458{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002459 // Note: Deviated from redfish privilege registry for GET & HEAD
2460 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002461
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002462 BMCWEB_ROUTE(
2463 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002464 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002465 .methods(boost::beast::http::verb::get)(
2466 [](const crow::Request&,
2467 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2468 const std::string& param) {
2469 const std::string& logID = param;
2470 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2471 });
2472}
Ed Tanous1da66f72018-07-27 16:13:37 -07002473
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002474inline void requestRoutesCrashdumpFile(App& app)
2475{
2476 // Note: Deviated from redfish privilege registry for GET & HEAD
2477 // method for security reasons.
2478 BMCWEB_ROUTE(
2479 app,
2480 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07002481 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002482 .methods(boost::beast::http::verb::get)(
2483 [](const crow::Request&,
2484 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2485 const std::string& logID, const std::string& fileName) {
2486 auto getStoredLogCallback =
2487 [asyncResp, logID, fileName](
2488 const boost::system::error_code ec,
2489 const std::vector<std::pair<std::string, VariantType>>&
2490 resp) {
2491 if (ec)
2492 {
2493 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2494 << ec.message();
2495 messages::internalError(asyncResp->res);
2496 return;
2497 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002498
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002499 std::string dbusFilename{};
2500 std::string dbusTimestamp{};
2501 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002502
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002503 parseCrashdumpParameters(resp, dbusFilename,
2504 dbusTimestamp, dbusFilepath);
2505
2506 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2507 dbusFilepath.empty())
2508 {
2509 messages::resourceMissingAtURI(asyncResp->res,
2510 fileName);
2511 return;
2512 }
2513
2514 // Verify the file name parameter is correct
2515 if (fileName != dbusFilename)
2516 {
2517 messages::resourceMissingAtURI(asyncResp->res,
2518 fileName);
2519 return;
2520 }
2521
2522 if (!std::filesystem::exists(dbusFilepath))
2523 {
2524 messages::resourceMissingAtURI(asyncResp->res,
2525 fileName);
2526 return;
2527 }
2528 std::ifstream ifs(dbusFilepath, std::ios::in |
2529 std::ios::binary |
2530 std::ios::ate);
2531 std::ifstream::pos_type fileSize = ifs.tellg();
2532 if (fileSize < 0)
2533 {
2534 messages::generalError(asyncResp->res);
2535 return;
2536 }
2537 ifs.seekg(0, std::ios::beg);
2538
2539 auto crashData = std::make_unique<char[]>(
2540 static_cast<unsigned int>(fileSize));
2541
2542 ifs.read(crashData.get(), static_cast<int>(fileSize));
2543
2544 // The cast to std::string is intentional in order to
2545 // use the assign() that applies move mechanics
2546 asyncResp->res.body().assign(
2547 static_cast<std::string>(crashData.get()));
2548
2549 // Configure this to be a file download when accessed
2550 // from a browser
2551 asyncResp->res.addHeader("Content-Disposition",
2552 "attachment");
2553 };
2554 crow::connections::systemBus->async_method_call(
2555 std::move(getStoredLogCallback), crashdumpObject,
2556 crashdumpPath + std::string("/") + logID,
2557 "org.freedesktop.DBus.Properties", "GetAll",
2558 crashdumpInterface);
2559 });
2560}
2561
2562inline void requestRoutesCrashdumpCollect(App& app)
2563{
2564 // Note: Deviated from redfish privilege registry for GET & HEAD
2565 // method for security reasons.
2566 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/"
2567 "Actions/LogService.CollectDiagnosticData/")
Ed Tanous432a8902021-06-14 15:28:56 -07002568 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002569 .methods(
2570 boost::beast::http::verb::
2571 post)([](const crow::Request& req,
2572 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2573 std::string diagnosticDataType;
2574 std::string oemDiagnosticDataType;
2575 if (!redfish::json_util::readJson(
2576 req, asyncResp->res, "DiagnosticDataType",
2577 diagnosticDataType, "OEMDiagnosticDataType",
2578 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002579 {
James Feist46229572020-02-19 15:11:58 -08002580 return;
2581 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002582
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002583 if (diagnosticDataType != "OEM")
2584 {
2585 BMCWEB_LOG_ERROR
2586 << "Only OEM DiagnosticDataType supported for Crashdump";
2587 messages::actionParameterValueFormatError(
2588 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2589 "CollectDiagnosticData");
2590 return;
2591 }
2592
2593 auto collectCrashdumpCallback = [asyncResp, req](
2594 const boost::system::error_code
2595 ec,
2596 const std::string&) {
2597 if (ec)
2598 {
2599 if (ec.value() ==
2600 boost::system::errc::operation_not_supported)
2601 {
2602 messages::resourceInStandby(asyncResp->res);
2603 }
2604 else if (ec.value() ==
2605 boost::system::errc::device_or_resource_busy)
2606 {
2607 messages::serviceTemporarilyUnavailable(asyncResp->res,
2608 "60");
2609 }
2610 else
2611 {
2612 messages::internalError(asyncResp->res);
2613 }
2614 return;
2615 }
2616 std::shared_ptr<task::TaskData> task =
2617 task::TaskData::createTask(
2618 [](boost::system::error_code err,
2619 sdbusplus::message::message&,
2620 const std::shared_ptr<task::TaskData>& taskData) {
2621 if (!err)
2622 {
2623 taskData->messages.emplace_back(
2624 messages::taskCompletedOK(
2625 std::to_string(taskData->index)));
2626 taskData->state = "Completed";
2627 }
2628 return task::completed;
2629 },
2630 "type='signal',interface='org.freedesktop.DBus."
2631 "Properties',"
2632 "member='PropertiesChanged',arg0namespace='com.intel."
2633 "crashdump'");
2634 task->startTimer(std::chrono::minutes(5));
2635 task->populateResp(asyncResp->res);
2636 task->payload.emplace(req);
2637 };
2638
2639 if (oemDiagnosticDataType == "OnDemand")
2640 {
2641 crow::connections::systemBus->async_method_call(
2642 std::move(collectCrashdumpCallback), crashdumpObject,
2643 crashdumpPath, crashdumpOnDemandInterface,
2644 "GenerateOnDemandLog");
2645 }
2646 else if (oemDiagnosticDataType == "Telemetry")
2647 {
2648 crow::connections::systemBus->async_method_call(
2649 std::move(collectCrashdumpCallback), crashdumpObject,
2650 crashdumpPath, crashdumpTelemetryInterface,
2651 "GenerateTelemetryLog");
2652 }
2653 else
2654 {
2655 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2656 << oemDiagnosticDataType;
2657 messages::actionParameterValueFormatError(
2658 asyncResp->res, oemDiagnosticDataType,
2659 "OEMDiagnosticDataType", "CollectDiagnosticData");
2660 return;
2661 }
2662 });
2663}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002664
Andrew Geisslercb92c032018-08-17 07:56:14 -07002665/**
2666 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2667 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002668inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002669{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002670 /**
2671 * Function handles POST method request.
2672 * The Clear Log actions does not require any parameter.The action deletes
2673 * all entries found in the Entries collection for this Log Service.
2674 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002675
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002676 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
2677 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002678 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002679 .methods(boost::beast::http::verb::post)(
2680 [](const crow::Request&,
2681 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2682 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07002683
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002684 // Process response from Logging service.
2685 auto respHandler = [asyncResp](
2686 const boost::system::error_code ec) {
2687 BMCWEB_LOG_DEBUG
2688 << "doClearLog resp_handler callback: Done";
2689 if (ec)
2690 {
2691 // TODO Handle for specific error code
2692 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
2693 << ec;
2694 asyncResp->res.result(
2695 boost::beast::http::status::internal_server_error);
2696 return;
2697 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07002698
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002699 asyncResp->res.result(
2700 boost::beast::http::status::no_content);
2701 };
2702
2703 // Make call to Logging service to request Clear Log
2704 crow::connections::systemBus->async_method_call(
2705 respHandler, "xyz.openbmc_project.Logging",
2706 "/xyz/openbmc_project/logging",
2707 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2708 });
2709}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002710
2711/****************************************************
2712 * Redfish PostCode interfaces
2713 * using DBUS interface: getPostCodesTS
2714 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002715inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002716{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002717 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanous432a8902021-06-14 15:28:56 -07002718 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002719 .methods(boost::beast::http::verb::get)(
2720 [](const crow::Request&,
2721 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2722 asyncResp->res.jsonValue = {
2723 {"@odata.id",
2724 "/redfish/v1/Systems/system/LogServices/PostCodes"},
2725 {"@odata.type", "#LogService.v1_1_0.LogService"},
2726 {"Name", "POST Code Log Service"},
2727 {"Description", "POST Code Log Service"},
2728 {"Id", "BIOS POST Code Log"},
2729 {"OverWritePolicy", "WrapsWhenFull"},
2730 {"Entries",
2731 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2732 "PostCodes/Entries"}}}};
2733 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2734 {"target",
2735 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2736 "Actions/LogService.ClearLog"}};
2737 });
2738}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002739
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002740inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002741{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002742 BMCWEB_ROUTE(app,
2743 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2744 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07002745 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002746 .methods(boost::beast::http::verb::post)(
2747 [](const crow::Request&,
2748 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2749 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08002750
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002751 // Make call to post-code service to request clear all
2752 crow::connections::systemBus->async_method_call(
2753 [asyncResp](const boost::system::error_code ec) {
2754 if (ec)
2755 {
2756 // TODO Handle for specific error code
2757 BMCWEB_LOG_ERROR
2758 << "doClearPostCodes resp_handler got error "
2759 << ec;
2760 asyncResp->res.result(boost::beast::http::status::
2761 internal_server_error);
2762 messages::internalError(asyncResp->res);
2763 return;
2764 }
2765 },
2766 "xyz.openbmc_project.State.Boot.PostCode0",
2767 "/xyz/openbmc_project/State/Boot/PostCode0",
2768 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2769 });
2770}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002771
2772static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08002773 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302774 const boost::container::flat_map<
2775 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002776 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2777 const uint64_t skip = 0, const uint64_t top = 0)
2778{
2779 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002780 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05302781 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08002782
2783 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002784 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08002785
2786 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302787 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2788 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002789 {
2790 currentCodeIndex++;
2791 std::string postcodeEntryID =
2792 "B" + std::to_string(bootIndex) + "-" +
2793 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2794
2795 uint64_t usecSinceEpoch = code.first;
2796 uint64_t usTimeOffset = 0;
2797
2798 if (1 == currentCodeIndex)
2799 { // already incremented
2800 firstCodeTimeUs = code.first;
2801 }
2802 else
2803 {
2804 usTimeOffset = code.first - firstCodeTimeUs;
2805 }
2806
2807 // skip if no specific codeIndex is specified and currentCodeIndex does
2808 // not fall between top and skip
2809 if ((codeIndex == 0) &&
2810 (currentCodeIndex <= skip || currentCodeIndex > top))
2811 {
2812 continue;
2813 }
2814
Gunnar Mills4e0453b2020-07-08 14:00:30 -05002815 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08002816 // currentIndex
2817 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2818 {
2819 // This is done for simplicity. 1st entry is needed to calculate
2820 // time offset. To improve efficiency, one can get to the entry
2821 // directly (possibly with flatmap's nth method)
2822 continue;
2823 }
2824
2825 // currentCodeIndex is within top and skip or equal to specified code
2826 // index
2827
2828 // Get the Created time from the timestamp
2829 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05002830 entryTimeStr = crow::utility::getDateTime(
2831 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08002832
2833 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2834 std::ostringstream hexCode;
2835 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302836 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08002837 std::ostringstream timeOffsetStr;
2838 // Set Fixed -Point Notation
2839 timeOffsetStr << std::fixed;
2840 // Set precision to 4 digits
2841 timeOffsetStr << std::setprecision(4);
2842 // Add double to stream
2843 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2844 std::vector<std::string> messageArgs = {
2845 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2846
2847 // Get MessageArgs template from message registry
2848 std::string msg;
2849 if (message != nullptr)
2850 {
2851 msg = message->message;
2852
2853 // fill in this post code value
2854 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002855 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002856 {
2857 std::string argStr = "%" + std::to_string(++i);
2858 size_t argPos = msg.find(argStr);
2859 if (argPos != std::string::npos)
2860 {
2861 msg.replace(argPos, argStr.length(), messageArg);
2862 }
2863 }
2864 }
2865
Tim Leed4342a92020-04-27 11:47:58 +08002866 // Get Severity template from message registry
2867 std::string severity;
2868 if (message != nullptr)
2869 {
2870 severity = message->severity;
2871 }
2872
ZhikuiRena3316fc2020-01-29 14:58:08 -08002873 // add to AsyncResp
2874 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002875 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0ef217f2021-03-08 14:24:46 +08002876 bmcLogEntry = {{"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Gunnar Mills743e9a12020-10-26 12:44:53 -05002877 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2878 "PostCodes/Entries/" +
2879 postcodeEntryID},
2880 {"Name", "POST Code Log Entry"},
2881 {"Id", postcodeEntryID},
2882 {"Message", std::move(msg)},
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05302883 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
Gunnar Mills743e9a12020-10-26 12:44:53 -05002884 {"MessageArgs", std::move(messageArgs)},
2885 {"EntryType", "Event"},
2886 {"Severity", std::move(severity)},
2887 {"Created", entryTimeStr}};
George Liu0ef217f2021-03-08 14:24:46 +08002888
2889 if (std::get<std::vector<uint8_t>>(code.second).size())
2890 {
2891 bmcLogEntry["AdditionalDataURI"] =
2892 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
2893 postcodeEntryID + "/attachment";
2894 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08002895 }
2896}
2897
zhanghch058d1b46d2021-04-01 11:18:24 +08002898static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002899 const uint16_t bootIndex,
2900 const uint64_t codeIndex)
2901{
2902 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302903 [aResp, bootIndex,
2904 codeIndex](const boost::system::error_code ec,
2905 const boost::container::flat_map<
2906 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2907 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002908 if (ec)
2909 {
2910 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2911 messages::internalError(aResp->res);
2912 return;
2913 }
2914
2915 // skip the empty postcode boots
2916 if (postcode.empty())
2917 {
2918 return;
2919 }
2920
2921 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2922
2923 aResp->res.jsonValue["Members@odata.count"] =
2924 aResp->res.jsonValue["Members"].size();
2925 },
Jonathan Doman15124762021-01-07 17:54:17 -08002926 "xyz.openbmc_project.State.Boot.PostCode0",
2927 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08002928 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2929 bootIndex);
2930}
2931
zhanghch058d1b46d2021-04-01 11:18:24 +08002932static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002933 const uint16_t bootIndex,
2934 const uint16_t bootCount,
2935 const uint64_t entryCount, const uint64_t skip,
2936 const uint64_t top)
2937{
2938 crow::connections::systemBus->async_method_call(
2939 [aResp, bootIndex, bootCount, entryCount, skip,
2940 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302941 const boost::container::flat_map<
2942 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2943 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002944 if (ec)
2945 {
2946 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2947 messages::internalError(aResp->res);
2948 return;
2949 }
2950
2951 uint64_t endCount = entryCount;
2952 if (!postcode.empty())
2953 {
2954 endCount = entryCount + postcode.size();
2955
2956 if ((skip < endCount) && ((top + skip) > entryCount))
2957 {
2958 uint64_t thisBootSkip =
2959 std::max(skip, entryCount) - entryCount;
2960 uint64_t thisBootTop =
2961 std::min(top + skip, endCount) - entryCount;
2962
2963 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2964 thisBootSkip, thisBootTop);
2965 }
2966 aResp->res.jsonValue["Members@odata.count"] = endCount;
2967 }
2968
2969 // continue to previous bootIndex
2970 if (bootIndex < bootCount)
2971 {
2972 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2973 bootCount, endCount, skip, top);
2974 }
2975 else
2976 {
2977 aResp->res.jsonValue["Members@odata.nextLink"] =
2978 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2979 "Entries?$skip=" +
2980 std::to_string(skip + top);
2981 }
2982 },
Jonathan Doman15124762021-01-07 17:54:17 -08002983 "xyz.openbmc_project.State.Boot.PostCode0",
2984 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08002985 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2986 bootIndex);
2987}
2988
zhanghch058d1b46d2021-04-01 11:18:24 +08002989static void
2990 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
2991 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002992{
2993 uint64_t entryCount = 0;
2994 crow::connections::systemBus->async_method_call(
2995 [aResp, entryCount, skip,
2996 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002997 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002998 if (ec)
2999 {
3000 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3001 messages::internalError(aResp->res);
3002 return;
3003 }
3004 auto pVal = std::get_if<uint16_t>(&bootCount);
3005 if (pVal)
3006 {
3007 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3008 }
3009 else
3010 {
3011 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3012 }
3013 },
Jonathan Doman15124762021-01-07 17:54:17 -08003014 "xyz.openbmc_project.State.Boot.PostCode0",
3015 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003016 "org.freedesktop.DBus.Properties", "Get",
3017 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3018}
3019
George Liuaf61db12021-03-08 19:36:32 +08003020inline static bool parsePostCode(std::string postCodeID, uint64_t& currentValue,
3021 uint16_t& index)
3022{
3023 // postCodeID = B1-1
3024 std::vector<std::string> split;
3025 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3026 if (split.size() != 2 || split[0].length() < 2)
3027 {
3028 return false;
3029 }
3030
3031 const char* start = split[0].data() + 1;
3032 const char* end = split[0].data() + split[0].size();
3033 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3034
3035 if (ptrIndex != end || ecIndex != std::errc() || !index)
3036 {
3037 return false;
3038 }
3039
3040 start = split[1].data();
3041 end = split[1].data() + split[1].size();
3042 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3043 if (ptrValue != end || ecValue != std::errc() || !currentValue)
3044 {
3045 return false;
3046 }
3047
3048 return true;
3049}
3050
3051inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3052{
3053 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/"
3054 "Entries/<str>/attachment/")
3055 .privileges({"Login"})
3056 .methods(boost::beast::http::verb::get)(
3057 [](const crow::Request& req,
3058 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3059 const std::string& postCodeID) {
3060 if (!http_helpers::isOctetAccepted(req))
3061 {
3062 asyncResp->res.result(
3063 boost::beast::http::status::bad_request);
3064 return;
3065 }
3066
3067 uint64_t currentValue = 0;
3068 uint16_t index = 0;
3069 if (!parsePostCode(postCodeID, currentValue, index))
3070 {
3071 messages::resourceNotFound(asyncResp->res, "LogEntry",
3072 postCodeID);
3073 return;
3074 }
3075
3076 crow::connections::systemBus->async_method_call(
3077 [asyncResp, postCodeID, currentValue](
3078 const boost::system::error_code ec,
3079 const std::vector<std::tuple<
3080 uint64_t, std::vector<uint8_t>>>& postcodes) {
3081 if (ec.value() == EBADR)
3082 {
3083 messages::resourceNotFound(asyncResp->res,
3084 "LogEntry", postCodeID);
3085 return;
3086 }
3087 if (ec)
3088 {
3089 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3090 messages::internalError(asyncResp->res);
3091 return;
3092 }
3093
3094 if (currentValue < 1 ||
3095 postcodes.size() <= currentValue)
3096 {
3097 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3098 messages::resourceNotFound(asyncResp->res,
3099 "LogEntry", postCodeID);
3100 return;
3101 }
3102
3103 size_t value = static_cast<size_t>(currentValue) - 1;
3104 auto& [tID, code] = postcodes[value];
3105
3106 if (code.size() == 0)
3107 {
3108 BMCWEB_LOG_INFO << "No found post code data";
3109 messages::internalError(asyncResp->res);
3110 return;
3111 }
3112
3113 std::string_view strData(
3114 reinterpret_cast<const char*>(code.data()),
3115 code.size());
3116
3117 asyncResp->res.addHeader("Content-Type",
3118 "application/octet-stream");
3119 asyncResp->res.addHeader("Content-Transfer-Encoding",
3120 "Base64");
3121 asyncResp->res.body() =
3122 std::move(crow::utility::base64encode(strData));
3123 },
3124 "xyz.openbmc_project.State.Boot.PostCode0",
3125 "/xyz/openbmc_project/State/Boot/PostCode0",
3126 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3127 index);
3128 });
3129}
3130
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003131inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003132{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003133 BMCWEB_ROUTE(app,
3134 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanous432a8902021-06-14 15:28:56 -07003135 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003136 .methods(boost::beast::http::verb::get)(
3137 [](const crow::Request& req,
3138 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3139 asyncResp->res.jsonValue["@odata.type"] =
3140 "#LogEntryCollection.LogEntryCollection";
3141 asyncResp->res.jsonValue["@odata.id"] =
3142 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3143 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3144 asyncResp->res.jsonValue["Description"] =
3145 "Collection of POST Code Log Entries";
3146 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3147 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003148
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003149 uint64_t skip = 0;
3150 uint64_t top = maxEntriesPerPage; // Show max entries by default
3151 if (!getSkipParam(asyncResp, req, skip))
3152 {
3153 return;
3154 }
3155 if (!getTopParam(asyncResp, req, top))
3156 {
3157 return;
3158 }
3159 getCurrentBootNumber(asyncResp, skip, top);
3160 });
3161}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003162
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003163inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003164{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003165 BMCWEB_ROUTE(
3166 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanous432a8902021-06-14 15:28:56 -07003167 .privileges({{"Login"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003168 .methods(boost::beast::http::verb::get)(
3169 [](const crow::Request&,
3170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3171 const std::string& targetID) {
3172 size_t bootPos = targetID.find('B');
3173 if (bootPos == std::string::npos)
3174 {
3175 // Requested ID was not found
3176 messages::resourceMissingAtURI(asyncResp->res, targetID);
3177 return;
3178 }
3179 std::string_view bootIndexStr(targetID);
3180 bootIndexStr.remove_prefix(bootPos + 1);
3181 uint16_t bootIndex = 0;
3182 uint64_t codeIndex = 0;
3183 size_t dashPos = bootIndexStr.find('-');
ZhikuiRena3316fc2020-01-29 14:58:08 -08003184
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003185 if (dashPos == std::string::npos)
3186 {
3187 return;
3188 }
3189 std::string_view codeIndexStr(bootIndexStr);
3190 bootIndexStr.remove_suffix(dashPos);
3191 codeIndexStr.remove_prefix(dashPos + 1);
zhanghch058d1b46d2021-04-01 11:18:24 +08003192
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003193 bootIndex = static_cast<uint16_t>(
3194 strtoul(std::string(bootIndexStr).c_str(), nullptr, 0));
3195 codeIndex =
3196 strtoul(std::string(codeIndexStr).c_str(), nullptr, 0);
3197 if (bootIndex == 0 || codeIndex == 0)
3198 {
3199 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3200 << targetID;
3201 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003202
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003203 asyncResp->res.jsonValue["@odata.type"] =
3204 "#LogEntry.v1_4_0.LogEntry";
3205 asyncResp->res.jsonValue["@odata.id"] =
3206 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3207 "Entries";
3208 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3209 asyncResp->res.jsonValue["Description"] =
3210 "Collection of POST Code Log Entries";
3211 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3212 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003213
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003214 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3215 });
3216}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003217
Ed Tanous1da66f72018-07-27 16:13:37 -07003218} // namespace redfish