blob: 0d6be71e42019b192c3edc661004955de08b84dc [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
Spencer Kub7028eb2021-10-26 15:27:35 +080018#include "gzfile.hpp"
George Liu647b3cd2021-07-05 12:43:56 +080019#include "http_utility.hpp"
Spencer Kub7028eb2021-10-26 15:27:35 +080020#include "human_sort.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070021#include "registries.hpp"
22#include "registries/base_message_registry.hpp"
23#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080024#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070025
Jason M. Billse1f26342018-07-18 12:12:00 -070026#include <systemd/sd-journal.h>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060027#include <unistd.h>
Jason M. Billse1f26342018-07-18 12:12:00 -070028
John Edward Broadbent7e860f12021-04-08 15:57:16 -070029#include <app.hpp>
Ed Tanous9896eae2022-07-23 15:07:33 -070030#include <boost/algorithm/string/case_conv.hpp>
Ed Tanous11ba3972022-07-11 09:50:41 -070031#include <boost/algorithm/string/classification.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060032#include <boost/algorithm/string/replace.hpp>
Jason M. Bills4851d452019-03-28 11:27:48 -070033#include <boost/algorithm/string/split.hpp>
Ed Tanous07c8c202022-07-11 10:08:08 -070034#include <boost/beast/http/verb.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070035#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080036#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080037#include <dbus_utility.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070038#include <error_messages.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070039#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070040#include <registries/privilege_registry.hpp>
Ed Tanous2b829372022-08-03 14:22:34 -070041#include <utils/time_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050042
George Liu647b3cd2021-07-05 12:43:56 +080043#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070044#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080045#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070046#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070047#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080048#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070049
50namespace redfish
51{
52
Gunnar Mills1214b7e2020-06-04 10:11:30 -050053constexpr char const* crashdumpObject = "com.intel.crashdump";
54constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050055constexpr char const* crashdumpInterface = "com.intel.crashdump";
56constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070057 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050058constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070059 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070060constexpr char const* crashdumpTelemetryInterface =
61 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070062
Ed Tanousfffb8c12022-02-07 23:53:03 -080063namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -070064{
Ed Tanous26702d02021-11-03 15:02:33 -070065static const Message*
66 getMessageFromRegistry(const std::string& messageKey,
67 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070068{
Ed Tanous002d39b2022-05-31 08:59:27 -070069 std::span<const MessageEntry>::iterator messageIt =
70 std::find_if(registry.begin(), registry.end(),
71 [&messageKey](const MessageEntry& messageEntry) {
72 return std::strcmp(messageEntry.first, messageKey.c_str()) == 0;
Ed Tanous26702d02021-11-03 15:02:33 -070073 });
74 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070075 {
76 return &messageIt->second;
77 }
78
79 return nullptr;
80}
81
Gunnar Mills1214b7e2020-06-04 10:11:30 -050082static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070083{
84 // Redfish MessageIds are in the form
85 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
86 // the right Message
87 std::vector<std::string> fields;
88 fields.reserve(4);
89 boost::split(fields, messageID, boost::is_any_of("."));
Ed Tanous02cad962022-06-30 16:50:15 -070090 const std::string& registryName = fields[0];
91 const std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070092
93 // Find the right registry and check it for the MessageKey
94 if (std::string(base::header.registryPrefix) == registryName)
95 {
96 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070097 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070098 }
99 if (std::string(openbmc::header.registryPrefix) == registryName)
100 {
101 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -0700102 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -0700103 }
104 return nullptr;
105}
Ed Tanousfffb8c12022-02-07 23:53:03 -0800106} // namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -0700107
James Feistf6150402019-01-08 10:36:20 -0800108namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700109
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500110inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700111{
Ed Tanousd4d25792020-09-29 15:15:03 -0700112 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
113 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
114 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
115 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700116 {
117 return "Critical";
118 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700119 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
120 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
121 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700122 {
123 return "OK";
124 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700125 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700126 {
127 return "Warning";
128 }
129 return "";
130}
131
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700132inline static int getJournalMetadata(sd_journal* journal,
133 const std::string_view& field,
134 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700135{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500136 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700137 size_t length = 0;
138 int ret = 0;
139 // Get the metadata from the requested field of the journal entry
Ed Tanous46ff87b2022-01-07 09:25:51 -0800140 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
141 const void** dataVoid = reinterpret_cast<const void**>(&data);
142
143 ret = sd_journal_get_data(journal, field.data(), dataVoid, &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 }
Ed Tanous2b829372022-08-03 14:22:34 -0700182 entryTimestamp =
183 redfish::time_utils::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500184 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800185}
Ed Tanous50b8a432022-02-03 16:29:50 -0800186
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700187inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
188 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700189{
190 int ret = 0;
191 static uint64_t prevTs = 0;
192 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700193 if (firstEntry)
194 {
195 prevTs = 0;
196 }
197
Jason M. Bills16428a12018-11-02 12:42:29 -0700198 // Get the entry timestamp
199 uint64_t curTs = 0;
200 ret = sd_journal_get_realtime_usec(journal, &curTs);
201 if (ret < 0)
202 {
203 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
204 << strerror(-ret);
205 return false;
206 }
207 // If the timestamp isn't unique, increment the index
208 if (curTs == prevTs)
209 {
210 index++;
211 }
212 else
213 {
214 // Otherwise, reset it
215 index = 0;
216 }
217 // Save the timestamp
218 prevTs = curTs;
219
220 entryID = std::to_string(curTs);
221 if (index > 0)
222 {
223 entryID += "_" + std::to_string(index);
224 }
225 return true;
226}
227
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500228static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700229 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700230{
Ed Tanous271584a2019-07-09 16:24:22 -0700231 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700232 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700233 if (firstEntry)
234 {
235 prevTs = 0;
236 }
237
Jason M. Bills95820182019-04-22 16:25:34 -0700238 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700239 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700240 std::tm timeStruct = {};
241 std::istringstream entryStream(logEntry);
242 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
243 {
244 curTs = std::mktime(&timeStruct);
245 }
246 // If the timestamp isn't unique, increment the index
247 if (curTs == prevTs)
248 {
249 index++;
250 }
251 else
252 {
253 // Otherwise, reset it
254 index = 0;
255 }
256 // Save the timestamp
257 prevTs = curTs;
258
259 entryID = std::to_string(curTs);
260 if (index > 0)
261 {
262 entryID += "_" + std::to_string(index);
263 }
264 return true;
265}
266
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700267inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800268 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
269 const std::string& entryID, uint64_t& timestamp,
270 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700271{
272 if (entryID.empty())
273 {
274 return false;
275 }
276 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800277 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700278
Ed Tanous81ce6092020-12-17 16:54:55 +0000279 auto underscorePos = tsStr.find('_');
Ed Tanous71d5d8d2022-01-25 11:04:33 -0800280 if (underscorePos != std::string_view::npos)
Jason M. Bills16428a12018-11-02 12:42:29 -0700281 {
282 // Timestamp has an index
283 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800284 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700285 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700286 auto [ptr, ec] = std::from_chars(
287 indexStr.data(), indexStr.data() + indexStr.size(), index);
288 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700289 {
Ed Tanousace85d62021-10-26 12:45:59 -0700290 messages::resourceMissingAtURI(
291 asyncResp->res, crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700292 return false;
293 }
294 }
295 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700296 auto [ptr, ec] =
297 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
298 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700299 {
Ed Tanousace85d62021-10-26 12:45:59 -0700300 messages::resourceMissingAtURI(asyncResp->res,
301 crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700302 return false;
303 }
304 return true;
305}
306
Jason M. Bills95820182019-04-22 16:25:34 -0700307static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500308 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700309{
310 static const std::filesystem::path redfishLogDir = "/var/log";
311 static const std::string redfishLogFilename = "redfish";
312
313 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500314 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700315 std::filesystem::directory_iterator(redfishLogDir))
316 {
317 // If we find a redfish log file, save the path
318 std::string filename = dirEnt.path().filename();
Ed Tanous11ba3972022-07-11 09:50:41 -0700319 if (filename.starts_with(redfishLogFilename))
Jason M. Bills95820182019-04-22 16:25:34 -0700320 {
321 redfishLogFiles.emplace_back(redfishLogDir / filename);
322 }
323 }
324 // As the log files rotate, they are appended with a ".#" that is higher for
325 // the older logs. Since we don't expect more than 10 log files, we
326 // can just sort the list to get them in order from newest to oldest
327 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
328
329 return !redfishLogFiles.empty();
330}
331
Claire Weinanaefe3782022-07-15 19:17:19 -0700332inline void parseDumpEntryFromDbusObject(
Jiaqing Zhao2d613eb2022-08-15 16:03:00 +0800333 const dbus::utility::ManagedObjectType::value_type& object,
334 std::string& dumpStatus, uint64_t& size, uint64_t& timestamp,
Claire Weinanaefe3782022-07-15 19:17:19 -0700335 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
336{
337 for (const auto& interfaceMap : object.second)
338 {
339 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
340 {
341 for (const auto& propertyMap : interfaceMap.second)
342 {
343 if (propertyMap.first == "Status")
344 {
345 const auto* status =
346 std::get_if<std::string>(&propertyMap.second);
347 if (status == nullptr)
348 {
349 messages::internalError(asyncResp->res);
350 break;
351 }
352 dumpStatus = *status;
353 }
354 }
355 }
356 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
357 {
358 for (const auto& propertyMap : interfaceMap.second)
359 {
360 if (propertyMap.first == "Size")
361 {
362 const auto* sizePtr =
363 std::get_if<uint64_t>(&propertyMap.second);
364 if (sizePtr == nullptr)
365 {
366 messages::internalError(asyncResp->res);
367 break;
368 }
369 size = *sizePtr;
370 break;
371 }
372 }
373 }
374 else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime")
375 {
376 for (const auto& propertyMap : interfaceMap.second)
377 {
378 if (propertyMap.first == "Elapsed")
379 {
380 const uint64_t* usecsTimeStamp =
381 std::get_if<uint64_t>(&propertyMap.second);
382 if (usecsTimeStamp == nullptr)
383 {
384 messages::internalError(asyncResp->res);
385 break;
386 }
Claire Weinanc21c64b2022-08-14 20:04:45 -0700387 timestamp = (*usecsTimeStamp / 1000 / 1000);
Claire Weinanaefe3782022-07-15 19:17:19 -0700388 break;
389 }
390 }
391 }
392 }
393}
394
Nan Zhou21ab4042022-06-26 23:07:40 +0000395static std::string getDumpEntriesPath(const std::string& dumpType)
Claire Weinanfdd26902022-03-01 14:18:25 -0800396{
397 std::string entriesPath;
398
399 if (dumpType == "BMC")
400 {
401 entriesPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
402 }
403 else if (dumpType == "FaultLog")
404 {
405 entriesPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/";
406 }
407 else if (dumpType == "System")
408 {
409 entriesPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
410 }
411 else
412 {
413 BMCWEB_LOG_ERROR << "getDumpEntriesPath() invalid dump type: "
414 << dumpType;
415 }
416
417 // Returns empty string on error
418 return entriesPath;
419}
420
zhanghch058d1b46d2021-04-01 11:18:24 +0800421inline void
422 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
423 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500424{
Claire Weinanfdd26902022-03-01 14:18:25 -0800425 std::string entriesPath = getDumpEntriesPath(dumpType);
426 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500427 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500428 messages::internalError(asyncResp->res);
429 return;
430 }
431
432 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800433 [asyncResp, entriesPath,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800434 dumpType](const boost::system::error_code ec,
435 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700436 if (ec)
437 {
438 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
439 messages::internalError(asyncResp->res);
440 return;
441 }
442
Claire Weinanfdd26902022-03-01 14:18:25 -0800443 // Remove ending slash
444 std::string odataIdStr = entriesPath;
445 if (!odataIdStr.empty())
446 {
447 odataIdStr.pop_back();
448 }
449
450 asyncResp->res.jsonValue["@odata.type"] =
451 "#LogEntryCollection.LogEntryCollection";
452 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
453 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
454 asyncResp->res.jsonValue["Description"] =
455 "Collection of " + dumpType + " Dump Entries";
456
Ed Tanous002d39b2022-05-31 08:59:27 -0700457 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
458 entriesArray = nlohmann::json::array();
459 std::string dumpEntryPath =
460 "/xyz/openbmc_project/dump/" +
461 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
462
463 std::sort(resp.begin(), resp.end(), [](const auto& l, const auto& r) {
464 return AlphanumLess<std::string>()(l.first.filename(),
465 r.first.filename());
466 });
467
468 for (auto& object : resp)
469 {
470 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500471 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700472 continue;
473 }
474 uint64_t timestamp = 0;
475 uint64_t size = 0;
476 std::string dumpStatus;
Jason M. Bills433b68b2022-06-28 12:24:26 -0700477 nlohmann::json::object_t thisEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -0700478
479 std::string entryID = object.first.filename();
480 if (entryID.empty())
481 {
482 continue;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500483 }
484
Claire Weinanaefe3782022-07-15 19:17:19 -0700485 parseDumpEntryFromDbusObject(object, dumpStatus, size, timestamp,
486 asyncResp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700487
488 if (dumpStatus !=
489 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
490 !dumpStatus.empty())
491 {
492 // Dump status is not Complete, no need to enumerate
493 continue;
494 }
495
496 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800497 thisEntry["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700498 thisEntry["Id"] = entryID;
499 thisEntry["EntryType"] = "Event";
Ed Tanous2b829372022-08-03 14:22:34 -0700500 thisEntry["Created"] =
501 redfish::time_utils::getDateTimeUint(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700502 thisEntry["Name"] = dumpType + " Dump Entry";
503
Ed Tanous002d39b2022-05-31 08:59:27 -0700504 if (dumpType == "BMC")
505 {
506 thisEntry["DiagnosticDataType"] = "Manager";
507 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800508 entriesPath + entryID + "/attachment";
509 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700510 }
511 else if (dumpType == "System")
512 {
513 thisEntry["DiagnosticDataType"] = "OEM";
514 thisEntry["OEMDiagnosticDataType"] = "System";
515 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800516 entriesPath + entryID + "/attachment";
517 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700518 }
519 entriesArray.push_back(std::move(thisEntry));
520 }
521 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500522 },
523 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
524 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
525}
526
zhanghch058d1b46d2021-04-01 11:18:24 +0800527inline void
Claire Weinanc7a6d662022-06-13 16:36:39 -0700528 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800529 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500530{
Claire Weinanfdd26902022-03-01 14:18:25 -0800531 std::string entriesPath = getDumpEntriesPath(dumpType);
532 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500533 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500534 messages::internalError(asyncResp->res);
535 return;
536 }
537
538 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800539 [asyncResp, entryID, dumpType,
540 entriesPath](const boost::system::error_code ec,
Ed Tanous02cad962022-06-30 16:50:15 -0700541 const dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700542 if (ec)
543 {
544 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
545 messages::internalError(asyncResp->res);
546 return;
547 }
548
549 bool foundDumpEntry = false;
550 std::string dumpEntryPath =
551 "/xyz/openbmc_project/dump/" +
552 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
553
554 for (const auto& objectPath : resp)
555 {
556 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500557 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700558 continue;
559 }
560
561 foundDumpEntry = true;
562 uint64_t timestamp = 0;
563 uint64_t size = 0;
564 std::string dumpStatus;
565
Claire Weinanaefe3782022-07-15 19:17:19 -0700566 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size,
567 timestamp, asyncResp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700568
569 if (dumpStatus !=
570 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
571 !dumpStatus.empty())
572 {
573 // Dump status is not Complete
574 // return not found until status is changed to Completed
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +0800575 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500576 return;
577 }
578
Ed Tanous002d39b2022-05-31 08:59:27 -0700579 asyncResp->res.jsonValue["@odata.type"] =
580 "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800581 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700582 asyncResp->res.jsonValue["Id"] = entryID;
583 asyncResp->res.jsonValue["EntryType"] = "Event";
584 asyncResp->res.jsonValue["Created"] =
Ed Tanous2b829372022-08-03 14:22:34 -0700585 redfish::time_utils::getDateTimeUint(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700586 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500587
Ed Tanous002d39b2022-05-31 08:59:27 -0700588 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500589 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700590 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
591 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800592 entriesPath + entryID + "/attachment";
593 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500594 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700595 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500596 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700597 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
598 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
599 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800600 entriesPath + entryID + "/attachment";
601 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500602 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700603 }
604 if (!foundDumpEntry)
605 {
606 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
607 messages::internalError(asyncResp->res);
608 return;
609 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500610 },
611 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
612 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
613}
614
zhanghch058d1b46d2021-04-01 11:18:24 +0800615inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800616 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500617 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500618{
Ed Tanous002d39b2022-05-31 08:59:27 -0700619 auto respHandler =
620 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500621 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
622 if (ec)
623 {
George Liu3de8d8b2021-03-22 17:49:39 +0800624 if (ec.value() == EBADR)
625 {
626 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
627 return;
628 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500629 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -0800630 << ec << " entryID=" << entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500631 messages::internalError(asyncResp->res);
632 return;
633 }
634 };
635 crow::connections::systemBus->async_method_call(
636 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500637 "/xyz/openbmc_project/dump/" +
638 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
639 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500640 "xyz.openbmc_project.Object.Delete", "Delete");
641}
642
zhanghch058d1b46d2021-04-01 11:18:24 +0800643inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700644 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800645 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
646 const uint32_t& dumpId, const std::string& dumpPath,
647 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500648{
649 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -0500650 [dumpId, dumpPath,
651 dumpType](boost::system::error_code err, sdbusplus::message_t& m,
652 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700653 if (err)
654 {
655 BMCWEB_LOG_ERROR << "Error in creating a dump";
656 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500657 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700658 }
659
660 dbus::utility::DBusInteracesMap interfacesList;
661
662 sdbusplus::message::object_path objPath;
663
664 m.read(objPath, interfacesList);
665
666 if (objPath.str ==
667 "/xyz/openbmc_project/dump/" +
668 std::string(boost::algorithm::to_lower_copy(dumpType)) +
669 "/entry/" + std::to_string(dumpId))
670 {
671 nlohmann::json retMessage = messages::success();
672 taskData->messages.emplace_back(retMessage);
673
674 std::string headerLoc =
675 "Location: " + dumpPath + std::to_string(dumpId);
676 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
677
678 taskData->state = "Completed";
679 return task::completed;
680 }
681 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500682 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800683 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500684 "member='InterfacesAdded', "
685 "path='/xyz/openbmc_project/dump'");
686
687 task->startTimer(std::chrono::minutes(3));
688 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700689 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500690}
691
zhanghch058d1b46d2021-04-01 11:18:24 +0800692inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
693 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500694{
Claire Weinanfdd26902022-03-01 14:18:25 -0800695 std::string dumpPath = getDumpEntriesPath(dumpType);
696 if (dumpPath.empty())
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500697 {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500698 messages::internalError(asyncResp->res);
699 return;
700 }
701
702 std::optional<std::string> diagnosticDataType;
703 std::optional<std::string> oemDiagnosticDataType;
704
Willy Tu15ed6782021-12-14 11:03:16 -0800705 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500706 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
707 "OEMDiagnosticDataType", oemDiagnosticDataType))
708 {
709 return;
710 }
711
712 if (dumpType == "System")
713 {
714 if (!oemDiagnosticDataType || !diagnosticDataType)
715 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800716 BMCWEB_LOG_ERROR
717 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500718 messages::actionParameterMissing(
719 asyncResp->res, "CollectDiagnosticData",
720 "DiagnosticDataType & OEMDiagnosticDataType");
721 return;
722 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700723 if ((*oemDiagnosticDataType != "System") ||
724 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500725 {
726 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700727 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500728 return;
729 }
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500730 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500731 }
732 else if (dumpType == "BMC")
733 {
734 if (!diagnosticDataType)
735 {
George Liu0fda0f12021-11-16 10:06:17 +0800736 BMCWEB_LOG_ERROR
737 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500738 messages::actionParameterMissing(
739 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
740 return;
741 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700742 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500743 {
744 BMCWEB_LOG_ERROR
745 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700746 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500747 return;
748 }
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500749 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/";
750 }
751 else
752 {
753 BMCWEB_LOG_ERROR << "CreateDump failed. Unknown dump type";
754 messages::internalError(asyncResp->res);
755 return;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500756 }
757
758 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700759 [asyncResp, payload(task::Payload(req)), dumpPath,
760 dumpType](const boost::system::error_code ec,
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500761 const sdbusplus::message::message& msg,
Ed Tanous98be3e32021-09-16 15:05:36 -0700762 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700763 if (ec)
764 {
765 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500766 const sd_bus_error* dbusError = msg.get_error();
767 if (dbusError == nullptr)
768 {
769 messages::internalError(asyncResp->res);
770 return;
771 }
772
773 BMCWEB_LOG_ERROR << "CreateDump DBus error: " << dbusError->name
774 << " and error msg: " << dbusError->message;
775 if (std::string_view(
776 "xyz.openbmc_project.Common.Error.NotAllowed") ==
777 dbusError->name)
778 {
779 messages::resourceInStandby(asyncResp->res);
780 return;
781 }
782 if (std::string_view(
783 "xyz.openbmc_project.Dump.Create.Error.Disabled") ==
784 dbusError->name)
785 {
786 messages::serviceDisabled(asyncResp->res, dumpPath);
787 return;
788 }
789 if (std::string_view(
790 "xyz.openbmc_project.Common.Error.Unavailable") ==
791 dbusError->name)
792 {
793 messages::resourceInUse(asyncResp->res);
794 return;
795 }
796 // Other Dbus errors such as:
797 // xyz.openbmc_project.Common.Error.InvalidArgument &
798 // org.freedesktop.DBus.Error.InvalidArgs are all related to
799 // the dbus call that is made here in the bmcweb
800 // implementation and has nothing to do with the client's
801 // input in the request. Hence, returning internal error
802 // back to the client.
Ed Tanous002d39b2022-05-31 08:59:27 -0700803 messages::internalError(asyncResp->res);
804 return;
805 }
806 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500807
Ed Tanous002d39b2022-05-31 08:59:27 -0700808 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
809 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500810 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500811 "xyz.openbmc_project.Dump.Manager",
812 "/xyz/openbmc_project/dump/" +
813 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500814 "xyz.openbmc_project.Dump.Create", "CreateDump");
815}
816
zhanghch058d1b46d2021-04-01 11:18:24 +0800817inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
818 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500819{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500820 std::string dumpTypeLowerCopy =
821 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800822
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500823 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800824 [asyncResp, dumpType](
825 const boost::system::error_code ec,
826 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700827 if (ec)
828 {
829 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
830 messages::internalError(asyncResp->res);
831 return;
832 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500833
Ed Tanous002d39b2022-05-31 08:59:27 -0700834 for (const std::string& path : subTreePaths)
835 {
836 sdbusplus::message::object_path objPath(path);
837 std::string logID = objPath.filename();
838 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500839 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700840 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500841 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700842 deleteDumpEntry(asyncResp, logID, dumpType);
843 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500844 },
845 "xyz.openbmc_project.ObjectMapper",
846 "/xyz/openbmc_project/object_mapper",
847 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500848 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
849 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
850 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500851}
852
Ed Tanousb9d36b42022-02-26 21:42:46 -0800853inline static void
854 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
855 std::string& filename, std::string& timestamp,
856 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700857{
858 for (auto property : params)
859 {
860 if (property.first == "Timestamp")
861 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500862 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500863 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700864 if (value != nullptr)
865 {
866 timestamp = *value;
867 }
868 }
869 else if (property.first == "Filename")
870 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500871 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500872 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700873 if (value != nullptr)
874 {
875 filename = *value;
876 }
877 }
878 else if (property.first == "Log")
879 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500880 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500881 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700882 if (value != nullptr)
883 {
884 logfile = *value;
885 }
886 }
887 }
888}
889
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500890constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700891inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700892{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800893 /**
894 * Functions triggers appropriate requests on DBus
895 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700896 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700897 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700898 .methods(boost::beast::http::verb::get)(
899 [&app](const crow::Request& req,
900 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000901 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700902 {
903 return;
904 }
905 // Collections don't include the static data added by SubRoute
906 // because it has a duplicate entry for members
907 asyncResp->res.jsonValue["@odata.type"] =
908 "#LogServiceCollection.LogServiceCollection";
909 asyncResp->res.jsonValue["@odata.id"] =
910 "/redfish/v1/Systems/system/LogServices";
911 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
912 asyncResp->res.jsonValue["Description"] =
913 "Collection of LogServices for this Computer System";
914 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
915 logServiceArray = nlohmann::json::array();
916 nlohmann::json::object_t eventLog;
917 eventLog["@odata.id"] =
918 "/redfish/v1/Systems/system/LogServices/EventLog";
919 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500920#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700921 nlohmann::json::object_t dumpLog;
922 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
923 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600924#endif
925
Jason M. Billsd53dd412019-02-12 17:16:22 -0800926#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700927 nlohmann::json::object_t crashdump;
928 crashdump["@odata.id"] =
929 "/redfish/v1/Systems/system/LogServices/Crashdump";
930 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800931#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800932
933#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700934 nlohmann::json::object_t hostlogger;
935 hostlogger["@odata.id"] =
936 "/redfish/v1/Systems/system/LogServices/HostLogger";
937 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800938#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700939 asyncResp->res.jsonValue["Members@odata.count"] =
940 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800941
Ed Tanous002d39b2022-05-31 08:59:27 -0700942 crow::connections::systemBus->async_method_call(
943 [asyncResp](const boost::system::error_code ec,
944 const dbus::utility::MapperGetSubTreePathsResponse&
945 subtreePath) {
946 if (ec)
947 {
948 BMCWEB_LOG_ERROR << ec;
949 return;
950 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700951
Ed Tanous002d39b2022-05-31 08:59:27 -0700952 for (const auto& pathStr : subtreePath)
953 {
954 if (pathStr.find("PostCode") != std::string::npos)
955 {
956 nlohmann::json& logServiceArrayLocal =
957 asyncResp->res.jsonValue["Members"];
958 logServiceArrayLocal.push_back(
959 {{"@odata.id",
960 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
961 asyncResp->res.jsonValue["Members@odata.count"] =
962 logServiceArrayLocal.size();
963 return;
964 }
965 }
966 },
967 "xyz.openbmc_project.ObjectMapper",
968 "/xyz/openbmc_project/object_mapper",
969 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
970 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700971 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700972}
973
974inline void requestRoutesEventLogService(App& app)
975{
976 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700977 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700978 .methods(boost::beast::http::verb::get)(
979 [&app](const crow::Request& req,
980 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000981 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700982 {
983 return;
984 }
985 asyncResp->res.jsonValue["@odata.id"] =
986 "/redfish/v1/Systems/system/LogServices/EventLog";
987 asyncResp->res.jsonValue["@odata.type"] =
988 "#LogService.v1_1_0.LogService";
989 asyncResp->res.jsonValue["Name"] = "Event Log Service";
990 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
991 asyncResp->res.jsonValue["Id"] = "EventLog";
992 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530993
Ed Tanous002d39b2022-05-31 08:59:27 -0700994 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -0700995 redfish::time_utils::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530996
Ed Tanous002d39b2022-05-31 08:59:27 -0700997 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
998 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
999 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05301000
Ed Tanous002d39b2022-05-31 08:59:27 -07001001 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1002 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1003 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001004
Ed Tanous002d39b2022-05-31 08:59:27 -07001005 {"target",
1006 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001007 });
1008}
1009
1010inline void requestRoutesJournalEventLogClear(App& app)
1011{
Jason M. Bills4978b632022-02-22 14:17:43 -08001012 BMCWEB_ROUTE(
1013 app,
1014 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001015 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001017 [&app](const crow::Request& req,
1018 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001019 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001020 {
1021 return;
1022 }
1023 // Clear the EventLog by deleting the log files
1024 std::vector<std::filesystem::path> redfishLogFiles;
1025 if (getRedfishLogFiles(redfishLogFiles))
1026 {
1027 for (const std::filesystem::path& file : redfishLogFiles)
1028 {
1029 std::error_code ec;
1030 std::filesystem::remove(file, ec);
1031 }
1032 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001033
Ed Tanous002d39b2022-05-31 08:59:27 -07001034 // Reload rsyslog so it knows to start new log files
1035 crow::connections::systemBus->async_method_call(
1036 [asyncResp](const boost::system::error_code ec) {
1037 if (ec)
1038 {
1039 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1040 messages::internalError(asyncResp->res);
1041 return;
1042 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001043
Ed Tanous002d39b2022-05-31 08:59:27 -07001044 messages::success(asyncResp->res);
1045 },
1046 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1047 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1048 "replace");
1049 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001050}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001051
Jason M. Billsac992cd2022-06-24 13:31:46 -07001052enum class LogParseError
1053{
1054 success,
1055 parseFailed,
1056 messageIdNotInRegistry,
1057};
1058
1059static LogParseError
1060 fillEventLogEntryJson(const std::string& logEntryID,
1061 const std::string& logEntry,
1062 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001063{
Jason M. Bills95820182019-04-22 16:25:34 -07001064 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001065 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001066 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001067 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001068 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001069 return LogParseError::parseFailed;
Jason M. Bills95820182019-04-22 16:25:34 -07001070 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001071 std::string timestamp = logEntry.substr(0, space);
1072 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001073 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001074 if (entryStart == std::string::npos)
1075 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001076 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001077 }
1078 std::string_view entry(logEntry);
1079 entry.remove_prefix(entryStart);
1080 // Use split to separate the entry into its fields
1081 std::vector<std::string> logEntryFields;
1082 boost::split(logEntryFields, entry, boost::is_any_of(","),
1083 boost::token_compress_on);
1084 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001085 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001086 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001087 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001088 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001089 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001090
Jason M. Bills4851d452019-03-28 11:27:48 -07001091 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001092 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001093
Sui Chen54417b02022-03-24 14:59:52 -07001094 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001095 {
Sui Chen54417b02022-03-24 14:59:52 -07001096 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001097 return LogParseError::messageIdNotInRegistry;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001098 }
1099
Sui Chen54417b02022-03-24 14:59:52 -07001100 std::string msg = message->message;
1101
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001102 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001103 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001104 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001105 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001106 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001107 // If the first string is empty, assume there are no MessageArgs
1108 std::size_t messageArgsSize = 0;
1109 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001110 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001111 messageArgsSize = logEntryFields.size() - 1;
1112 }
1113
Ed Tanous23a21a12020-07-25 04:45:05 +00001114 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001115
1116 // Fill the MessageArgs into the Message
1117 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001118 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001119 {
1120 std::string argStr = "%" + std::to_string(++i);
1121 size_t argPos = msg.find(argStr);
1122 if (argPos != std::string::npos)
1123 {
1124 msg.replace(argPos, argStr.length(), messageArg);
1125 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001126 }
1127 }
1128
Jason M. Bills95820182019-04-22 16:25:34 -07001129 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1130 // format which matches the Redfish format except for the fractional seconds
1131 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001132 std::size_t dot = timestamp.find_first_of('.');
1133 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001134 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001135 {
Jason M. Bills95820182019-04-22 16:25:34 -07001136 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001137 }
1138
1139 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07001140 logEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1141 logEntryJson["@odata.id"] =
1142 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1143 logEntryJson["Name"] = "System Event Log Entry";
1144 logEntryJson["Id"] = logEntryID;
1145 logEntryJson["Message"] = std::move(msg);
1146 logEntryJson["MessageId"] = std::move(messageID);
1147 logEntryJson["MessageArgs"] = messageArgs;
1148 logEntryJson["EntryType"] = "Event";
1149 logEntryJson["Severity"] = message->messageSeverity;
1150 logEntryJson["Created"] = std::move(timestamp);
Jason M. Billsac992cd2022-06-24 13:31:46 -07001151 return LogParseError::success;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001152}
1153
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001154inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001155{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001156 BMCWEB_ROUTE(app,
1157 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001158 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001159 .methods(boost::beast::http::verb::get)(
1160 [&app](const crow::Request& req,
1161 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1162 query_param::QueryCapabilities capabilities = {
1163 .canDelegateTop = true,
1164 .canDelegateSkip = true,
1165 };
1166 query_param::Query delegatedQuery;
1167 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001168 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001169 {
1170 return;
1171 }
Jiaqing Zhao5143f7a2022-07-22 09:33:33 +08001172 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
Ed Tanous3648c8b2022-07-25 13:39:59 -07001173 size_t skip = delegatedQuery.skip.value_or(0);
1174
Ed Tanous002d39b2022-05-31 08:59:27 -07001175 // Collections don't include the static data added by SubRoute
1176 // because it has a duplicate entry for members
1177 asyncResp->res.jsonValue["@odata.type"] =
1178 "#LogEntryCollection.LogEntryCollection";
1179 asyncResp->res.jsonValue["@odata.id"] =
1180 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1181 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1182 asyncResp->res.jsonValue["Description"] =
1183 "Collection of System Event Log Entries";
1184
1185 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1186 logEntryArray = nlohmann::json::array();
1187 // Go through the log files and create a unique ID for each
1188 // entry
1189 std::vector<std::filesystem::path> redfishLogFiles;
1190 getRedfishLogFiles(redfishLogFiles);
1191 uint64_t entryCount = 0;
1192 std::string logEntry;
1193
1194 // Oldest logs are in the last file, so start there and loop
1195 // backwards
1196 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1197 it++)
1198 {
1199 std::ifstream logStream(*it);
1200 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001201 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001202 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001203 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001204
Ed Tanous002d39b2022-05-31 08:59:27 -07001205 // Reset the unique ID on the first entry
1206 bool firstEntry = true;
1207 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001208 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001209 std::string idStr;
1210 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001211 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001212 continue;
1213 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001214 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001215
Jason M. Billsde703c52022-06-23 14:19:04 -07001216 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001217 LogParseError status =
1218 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1219 if (status == LogParseError::messageIdNotInRegistry)
1220 {
1221 continue;
1222 }
1223 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001224 {
1225 messages::internalError(asyncResp->res);
1226 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001227 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001228
Jason M. Billsde703c52022-06-23 14:19:04 -07001229 entryCount++;
1230 // Handle paging using skip (number of entries to skip from the
1231 // start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07001232 if (entryCount <= skip || entryCount > skip + top)
Jason M. Billsde703c52022-06-23 14:19:04 -07001233 {
1234 continue;
1235 }
1236
1237 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001238 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001239 }
1240 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001241 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07001242 {
1243 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1244 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001245 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07001246 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001247 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001248}
Chicago Duan336e96c2019-07-15 14:22:08 +08001249
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001250inline void requestRoutesJournalEventLogEntry(App& app)
1251{
1252 BMCWEB_ROUTE(
1253 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001254 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001255 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001256 [&app](const crow::Request& req,
1257 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1258 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001259 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001260 {
1261 return;
1262 }
1263 const std::string& targetID = param;
1264
1265 // Go through the log files and check the unique ID for each
1266 // entry to find the target entry
1267 std::vector<std::filesystem::path> redfishLogFiles;
1268 getRedfishLogFiles(redfishLogFiles);
1269 std::string logEntry;
1270
1271 // Oldest logs are in the last file, so start there and loop
1272 // backwards
1273 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1274 it++)
1275 {
1276 std::ifstream logStream(*it);
1277 if (!logStream.is_open())
1278 {
1279 continue;
1280 }
1281
1282 // Reset the unique ID on the first entry
1283 bool firstEntry = true;
1284 while (std::getline(logStream, logEntry))
1285 {
1286 std::string idStr;
1287 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001288 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001289 continue;
1290 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001291 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001292
1293 if (idStr == targetID)
1294 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001295 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001296 LogParseError status =
1297 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1298 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001299 {
1300 messages::internalError(asyncResp->res);
1301 return;
1302 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07001303 asyncResp->res.jsonValue.update(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001304 return;
1305 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001306 }
1307 }
1308 // Requested ID was not found
1309 messages::resourceMissingAtURI(asyncResp->res,
1310 crow::utility::urlFromPieces(targetID));
1311 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001312}
1313
1314inline void requestRoutesDBusEventLogEntryCollection(App& app)
1315{
1316 BMCWEB_ROUTE(app,
1317 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001318 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001319 .methods(boost::beast::http::verb::get)(
1320 [&app](const crow::Request& req,
1321 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001322 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001323 {
1324 return;
1325 }
1326 // Collections don't include the static data added by SubRoute
1327 // because it has a duplicate entry for members
1328 asyncResp->res.jsonValue["@odata.type"] =
1329 "#LogEntryCollection.LogEntryCollection";
1330 asyncResp->res.jsonValue["@odata.id"] =
1331 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1332 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1333 asyncResp->res.jsonValue["Description"] =
1334 "Collection of System Event Log Entries";
1335
1336 // DBus implementation of EventLog/Entries
1337 // Make call to Logging Service to find all log entry objects
1338 crow::connections::systemBus->async_method_call(
1339 [asyncResp](const boost::system::error_code ec,
1340 const dbus::utility::ManagedObjectType& resp) {
1341 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001342 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001343 // TODO Handle for specific error code
1344 BMCWEB_LOG_ERROR
1345 << "getLogEntriesIfaceData resp_handler got error " << ec;
1346 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001347 return;
1348 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001349 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1350 entriesArray = nlohmann::json::array();
1351 for (const auto& objectPath : resp)
1352 {
1353 const uint32_t* id = nullptr;
1354 const uint64_t* timestamp = nullptr;
1355 const uint64_t* updateTimestamp = nullptr;
1356 const std::string* severity = nullptr;
1357 const std::string* message = nullptr;
1358 const std::string* filePath = nullptr;
1359 bool resolved = false;
1360 for (const auto& interfaceMap : objectPath.second)
1361 {
1362 if (interfaceMap.first ==
1363 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001364 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001365 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001366 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001367 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001368 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001369 id = std::get_if<uint32_t>(&propertyMap.second);
1370 }
1371 else if (propertyMap.first == "Timestamp")
1372 {
1373 timestamp =
1374 std::get_if<uint64_t>(&propertyMap.second);
1375 }
1376 else if (propertyMap.first == "UpdateTimestamp")
1377 {
1378 updateTimestamp =
1379 std::get_if<uint64_t>(&propertyMap.second);
1380 }
1381 else if (propertyMap.first == "Severity")
1382 {
1383 severity = std::get_if<std::string>(
1384 &propertyMap.second);
1385 }
1386 else if (propertyMap.first == "Message")
1387 {
1388 message = std::get_if<std::string>(
1389 &propertyMap.second);
1390 }
1391 else if (propertyMap.first == "Resolved")
1392 {
1393 const bool* resolveptr =
1394 std::get_if<bool>(&propertyMap.second);
1395 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001396 {
1397 messages::internalError(asyncResp->res);
1398 return;
1399 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001400 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001401 }
1402 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001403 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001404 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001405 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001406 messages::internalError(asyncResp->res);
1407 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001408 }
1409 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001410 else if (interfaceMap.first ==
1411 "xyz.openbmc_project.Common.FilePath")
1412 {
1413 for (const 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 || timestamp == nullptr ||
1428 updateTimestamp == nullptr)
1429 {
1430 continue;
1431 }
1432 entriesArray.push_back({});
1433 nlohmann::json& thisEntry = entriesArray.back();
1434 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1435 thisEntry["@odata.id"] =
1436 "/redfish/v1/Systems/system/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"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001446 redfish::time_utils::getDateTimeUintMs(*timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001447 thisEntry["Modified"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001448 redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001449 if (filePath != nullptr)
1450 {
1451 thisEntry["AdditionalDataURI"] =
1452 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1453 std::to_string(*id) + "/attachment";
1454 }
1455 }
1456 std::sort(
1457 entriesArray.begin(), entriesArray.end(),
1458 [](const nlohmann::json& left, const nlohmann::json& right) {
1459 return (left["Id"] <= right["Id"]);
1460 });
1461 asyncResp->res.jsonValue["Members@odata.count"] =
1462 entriesArray.size();
1463 },
1464 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1465 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001466 });
1467}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001468
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001469inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001470{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001471 BMCWEB_ROUTE(
1472 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001473 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001474 .methods(boost::beast::http::verb::get)(
1475 [&app](const crow::Request& req,
1476 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1477 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001478 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001479 {
1480 return;
1481 }
1482 std::string entryID = param;
1483 dbus::utility::escapePathForDbus(entryID);
1484
1485 // 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 const dbus::utility::DBusPropertiesMap& resp) {
1490 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001491 {
Jiaqing Zhaod8a5d5d2022-08-05 16:21:51 +08001492 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001493 return;
1494 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001495 if (ec)
1496 {
1497 BMCWEB_LOG_ERROR
1498 << "EventLogEntry (DBus) resp_handler got error " << ec;
1499 messages::internalError(asyncResp->res);
1500 return;
1501 }
1502 const uint32_t* id = nullptr;
1503 const uint64_t* timestamp = nullptr;
1504 const uint64_t* updateTimestamp = nullptr;
1505 const std::string* severity = nullptr;
1506 const std::string* message = nullptr;
1507 const std::string* filePath = nullptr;
1508 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001509
Ed Tanous002d39b2022-05-31 08:59:27 -07001510 for (const auto& propertyMap : resp)
1511 {
1512 if (propertyMap.first == "Id")
1513 {
1514 id = std::get_if<uint32_t>(&propertyMap.second);
1515 }
1516 else if (propertyMap.first == "Timestamp")
1517 {
1518 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1519 }
1520 else if (propertyMap.first == "UpdateTimestamp")
1521 {
1522 updateTimestamp =
1523 std::get_if<uint64_t>(&propertyMap.second);
1524 }
1525 else if (propertyMap.first == "Severity")
1526 {
1527 severity = std::get_if<std::string>(&propertyMap.second);
1528 }
1529 else if (propertyMap.first == "Message")
1530 {
1531 message = std::get_if<std::string>(&propertyMap.second);
1532 }
1533 else if (propertyMap.first == "Resolved")
1534 {
1535 const bool* resolveptr =
1536 std::get_if<bool>(&propertyMap.second);
1537 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001538 {
1539 messages::internalError(asyncResp->res);
1540 return;
1541 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001542 resolved = *resolveptr;
1543 }
1544 else if (propertyMap.first == "Path")
1545 {
1546 filePath = std::get_if<std::string>(&propertyMap.second);
1547 }
1548 }
1549 if (id == nullptr || message == nullptr || severity == nullptr ||
1550 timestamp == nullptr || updateTimestamp == nullptr)
1551 {
1552 messages::internalError(asyncResp->res);
1553 return;
1554 }
1555 asyncResp->res.jsonValue["@odata.type"] =
1556 "#LogEntry.v1_8_0.LogEntry";
1557 asyncResp->res.jsonValue["@odata.id"] =
1558 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1559 std::to_string(*id);
1560 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1561 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1562 asyncResp->res.jsonValue["Message"] = *message;
1563 asyncResp->res.jsonValue["Resolved"] = resolved;
1564 asyncResp->res.jsonValue["EntryType"] = "Event";
1565 asyncResp->res.jsonValue["Severity"] =
1566 translateSeverityDbusToRedfish(*severity);
1567 asyncResp->res.jsonValue["Created"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001568 redfish::time_utils::getDateTimeUintMs(*timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001569 asyncResp->res.jsonValue["Modified"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001570 redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001571 if (filePath != nullptr)
1572 {
1573 asyncResp->res.jsonValue["AdditionalDataURI"] =
1574 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1575 std::to_string(*id) + "/attachment";
1576 }
1577 },
1578 "xyz.openbmc_project.Logging",
1579 "/xyz/openbmc_project/logging/entry/" + entryID,
1580 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001581 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001582
1583 BMCWEB_ROUTE(
1584 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001585 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001586 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001587 [&app](const crow::Request& req,
1588 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1589 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001590 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001591 {
1592 return;
1593 }
1594 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001595
Ed Tanous002d39b2022-05-31 08:59:27 -07001596 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1597 resolved))
1598 {
1599 return;
1600 }
1601 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001602
Ed Tanous002d39b2022-05-31 08:59:27 -07001603 crow::connections::systemBus->async_method_call(
1604 [asyncResp, entryId](const boost::system::error_code ec) {
1605 if (ec)
1606 {
1607 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1608 messages::internalError(asyncResp->res);
1609 return;
1610 }
1611 },
1612 "xyz.openbmc_project.Logging",
1613 "/xyz/openbmc_project/logging/entry/" + entryId,
1614 "org.freedesktop.DBus.Properties", "Set",
1615 "xyz.openbmc_project.Logging.Entry", "Resolved",
1616 dbus::utility::DbusVariantType(*resolved));
1617 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001618
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001619 BMCWEB_ROUTE(
1620 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001621 .privileges(redfish::privileges::deleteLogEntry)
1622
Ed Tanous002d39b2022-05-31 08:59:27 -07001623 .methods(boost::beast::http::verb::delete_)(
1624 [&app](const crow::Request& req,
1625 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1626 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001627 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001628 {
1629 return;
1630 }
1631 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1632
1633 std::string entryID = param;
1634
1635 dbus::utility::escapePathForDbus(entryID);
1636
1637 // Process response from Logging service.
1638 auto respHandler =
1639 [asyncResp, entryID](const boost::system::error_code ec) {
1640 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1641 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001642 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001643 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001644 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001645 messages::resourceNotFound(asyncResp->res, "LogEntry",
1646 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001647 return;
1648 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001649 // TODO Handle for specific error code
1650 BMCWEB_LOG_ERROR
1651 << "EventLogEntry (DBus) doDelete respHandler got error "
1652 << ec;
1653 asyncResp->res.result(
1654 boost::beast::http::status::internal_server_error);
1655 return;
1656 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001657
Ed Tanous002d39b2022-05-31 08:59:27 -07001658 asyncResp->res.result(boost::beast::http::status::ok);
1659 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660
Ed Tanous002d39b2022-05-31 08:59:27 -07001661 // Make call to Logging service to request Delete Log
1662 crow::connections::systemBus->async_method_call(
1663 respHandler, "xyz.openbmc_project.Logging",
1664 "/xyz/openbmc_project/logging/entry/" + entryID,
1665 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001666 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001667}
1668
1669inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001670{
George Liu0fda0f12021-11-16 10:06:17 +08001671 BMCWEB_ROUTE(
1672 app,
1673 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001674 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001675 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001676 [&app](const crow::Request& req,
1677 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1678 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001679 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001680 {
1681 return;
1682 }
1683 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1684 {
1685 asyncResp->res.result(boost::beast::http::status::bad_request);
1686 return;
1687 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001688
Ed Tanous002d39b2022-05-31 08:59:27 -07001689 std::string entryID = param;
1690 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001691
Ed Tanous002d39b2022-05-31 08:59:27 -07001692 crow::connections::systemBus->async_method_call(
1693 [asyncResp, entryID](const boost::system::error_code ec,
1694 const sdbusplus::message::unix_fd& unixfd) {
1695 if (ec.value() == EBADR)
1696 {
1697 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1698 entryID);
1699 return;
1700 }
1701 if (ec)
1702 {
1703 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1704 messages::internalError(asyncResp->res);
1705 return;
1706 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001707
Ed Tanous002d39b2022-05-31 08:59:27 -07001708 int fd = -1;
1709 fd = dup(unixfd);
1710 if (fd == -1)
1711 {
1712 messages::internalError(asyncResp->res);
1713 return;
1714 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001715
Ed Tanous002d39b2022-05-31 08:59:27 -07001716 long long int size = lseek(fd, 0, SEEK_END);
1717 if (size == -1)
1718 {
1719 messages::internalError(asyncResp->res);
1720 return;
1721 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001722
Ed Tanous002d39b2022-05-31 08:59:27 -07001723 // Arbitrary max size of 64kb
1724 constexpr int maxFileSize = 65536;
1725 if (size > maxFileSize)
1726 {
1727 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1728 << maxFileSize;
1729 messages::internalError(asyncResp->res);
1730 return;
1731 }
1732 std::vector<char> data(static_cast<size_t>(size));
1733 long long int rc = lseek(fd, 0, SEEK_SET);
1734 if (rc == -1)
1735 {
1736 messages::internalError(asyncResp->res);
1737 return;
1738 }
1739 rc = read(fd, data.data(), data.size());
1740 if ((rc == -1) || (rc != size))
1741 {
1742 messages::internalError(asyncResp->res);
1743 return;
1744 }
1745 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001746
Ed Tanous002d39b2022-05-31 08:59:27 -07001747 std::string_view strData(data.data(), data.size());
1748 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001749
Ed Tanousd9f6c622022-03-17 09:12:17 -07001750 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07001751 "application/octet-stream");
Ed Tanousd9f6c622022-03-17 09:12:17 -07001752 asyncResp->res.addHeader(
1753 boost::beast::http::field::content_transfer_encoding, "Base64");
Ed Tanous002d39b2022-05-31 08:59:27 -07001754 asyncResp->res.body() = std::move(output);
1755 },
1756 "xyz.openbmc_project.Logging",
1757 "/xyz/openbmc_project/logging/entry/" + entryID,
1758 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1759 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001760}
1761
Spencer Kub7028eb2021-10-26 15:27:35 +08001762constexpr const char* hostLoggerFolderPath = "/var/log/console";
1763
1764inline bool
1765 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1766 std::vector<std::filesystem::path>& hostLoggerFiles)
1767{
1768 std::error_code ec;
1769 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1770 if (ec)
1771 {
1772 BMCWEB_LOG_ERROR << ec.message();
1773 return false;
1774 }
1775 for (const std::filesystem::directory_entry& it : logPath)
1776 {
1777 std::string filename = it.path().filename();
1778 // Prefix of each log files is "log". Find the file and save the
1779 // path
Ed Tanous11ba3972022-07-11 09:50:41 -07001780 if (filename.starts_with("log"))
Spencer Kub7028eb2021-10-26 15:27:35 +08001781 {
1782 hostLoggerFiles.emplace_back(it.path());
1783 }
1784 }
1785 // As the log files rotate, they are appended with a ".#" that is higher for
1786 // the older logs. Since we start from oldest logs, sort the name in
1787 // descending order.
1788 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1789 AlphanumLess<std::string>());
1790
1791 return true;
1792}
1793
Ed Tanous02cad962022-06-30 16:50:15 -07001794inline bool getHostLoggerEntries(
1795 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1796 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001797{
1798 GzFileReader logFile;
1799
1800 // Go though all log files and expose host logs.
1801 for (const std::filesystem::path& it : hostLoggerFiles)
1802 {
1803 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1804 {
1805 BMCWEB_LOG_ERROR << "fail to expose host logs";
1806 return false;
1807 }
1808 }
1809 // Get lastMessage from constructor by getter
1810 std::string lastMessage = logFile.getLastMessage();
1811 if (!lastMessage.empty())
1812 {
1813 logCount++;
1814 if (logCount > skip && logCount <= (skip + top))
1815 {
1816 logEntries.push_back(lastMessage);
1817 }
1818 }
1819 return true;
1820}
1821
1822inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1823 const std::string& msg,
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001824 nlohmann::json::object_t& logEntryJson)
Spencer Kub7028eb2021-10-26 15:27:35 +08001825{
1826 // Fill in the log entry with the gathered data.
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001827 logEntryJson["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
1828 logEntryJson["@odata.id"] =
1829 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1830 logEntryID;
1831 logEntryJson["Name"] = "Host Logger Entry";
1832 logEntryJson["Id"] = logEntryID;
1833 logEntryJson["Message"] = msg;
1834 logEntryJson["EntryType"] = "Oem";
1835 logEntryJson["Severity"] = "OK";
1836 logEntryJson["OemRecordFormat"] = "Host Logger Entry";
Spencer Kub7028eb2021-10-26 15:27:35 +08001837}
1838
1839inline void requestRoutesSystemHostLogger(App& app)
1840{
1841 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1842 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001843 .methods(boost::beast::http::verb::get)(
1844 [&app](const crow::Request& req,
1845 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001846 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001847 {
1848 return;
1849 }
1850 asyncResp->res.jsonValue["@odata.id"] =
1851 "/redfish/v1/Systems/system/LogServices/HostLogger";
1852 asyncResp->res.jsonValue["@odata.type"] =
1853 "#LogService.v1_1_0.LogService";
1854 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1855 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1856 asyncResp->res.jsonValue["Id"] = "HostLogger";
1857 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1858 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1859 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001860}
1861
1862inline void requestRoutesSystemHostLoggerCollection(App& app)
1863{
1864 BMCWEB_ROUTE(app,
1865 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1866 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001867 .methods(boost::beast::http::verb::get)(
1868 [&app](const crow::Request& req,
1869 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1870 query_param::QueryCapabilities capabilities = {
1871 .canDelegateTop = true,
1872 .canDelegateSkip = true,
1873 };
1874 query_param::Query delegatedQuery;
1875 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001876 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001877 {
1878 return;
1879 }
1880 asyncResp->res.jsonValue["@odata.id"] =
1881 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1882 asyncResp->res.jsonValue["@odata.type"] =
1883 "#LogEntryCollection.LogEntryCollection";
1884 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1885 asyncResp->res.jsonValue["Description"] =
1886 "Collection of HostLogger Entries";
1887 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1888 logEntryArray = nlohmann::json::array();
1889 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001890
Ed Tanous002d39b2022-05-31 08:59:27 -07001891 std::vector<std::filesystem::path> hostLoggerFiles;
1892 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1893 {
1894 BMCWEB_LOG_ERROR << "fail to get host log file path";
1895 return;
1896 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001897 // If we weren't provided top and skip limits, use the defaults.
1898 size_t skip = delegatedQuery.skip.value_or(0);
Jiaqing Zhao5143f7a2022-07-22 09:33:33 +08001899 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
Ed Tanous002d39b2022-05-31 08:59:27 -07001900 size_t logCount = 0;
1901 // This vector only store the entries we want to expose that
1902 // control by skip and top.
1903 std::vector<std::string> logEntries;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001904 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1905 logCount))
Ed Tanous002d39b2022-05-31 08:59:27 -07001906 {
1907 messages::internalError(asyncResp->res);
1908 return;
1909 }
1910 // If vector is empty, that means skip value larger than total
1911 // log count
1912 if (logEntries.empty())
1913 {
1914 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1915 return;
1916 }
1917 if (!logEntries.empty())
1918 {
1919 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001920 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001921 nlohmann::json::object_t hostLogEntry;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001922 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
1923 hostLogEntry);
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001924 logEntryArray.push_back(std::move(hostLogEntry));
George Liu0fda0f12021-11-16 10:06:17 +08001925 }
1926
Ed Tanous002d39b2022-05-31 08:59:27 -07001927 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001928 if (skip + top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001929 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001930 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1931 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001932 std::to_string(skip + top);
George Liu0fda0f12021-11-16 10:06:17 +08001933 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001934 }
George Liu0fda0f12021-11-16 10:06:17 +08001935 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001936}
1937
1938inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1939{
1940 BMCWEB_ROUTE(
1941 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1942 .privileges(redfish::privileges::getLogEntry)
1943 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001944 [&app](const crow::Request& req,
1945 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1946 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001947 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001948 {
1949 return;
1950 }
1951 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001952
Ed Tanous002d39b2022-05-31 08:59:27 -07001953 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001954
Ed Tanous002d39b2022-05-31 08:59:27 -07001955 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1956 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001957
Ed Tanous002d39b2022-05-31 08:59:27 -07001958 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1959 if (ec == std::errc::invalid_argument)
1960 {
1961 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1962 return;
1963 }
1964 if (ec == std::errc::result_out_of_range)
1965 {
1966 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1967 return;
1968 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001969
Ed Tanous002d39b2022-05-31 08:59:27 -07001970 std::vector<std::filesystem::path> hostLoggerFiles;
1971 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1972 {
1973 BMCWEB_LOG_ERROR << "fail to get host log file path";
1974 return;
1975 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001976
Ed Tanous002d39b2022-05-31 08:59:27 -07001977 size_t logCount = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001978 size_t top = 1;
Ed Tanous002d39b2022-05-31 08:59:27 -07001979 std::vector<std::string> logEntries;
1980 // We can get specific entry by skip and top. For example, if we
1981 // want to get nth entry, we can set skip = n-1 and top = 1 to
1982 // get that entry
1983 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1984 logCount))
1985 {
1986 messages::internalError(asyncResp->res);
1987 return;
1988 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001989
Ed Tanous002d39b2022-05-31 08:59:27 -07001990 if (!logEntries.empty())
1991 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001992 nlohmann::json::object_t hostLogEntry;
1993 fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
1994 asyncResp->res.jsonValue.update(hostLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07001995 return;
1996 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001997
Ed Tanous002d39b2022-05-31 08:59:27 -07001998 // Requested ID was not found
1999 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2000 });
Spencer Kub7028eb2021-10-26 15:27:35 +08002001}
2002
Claire Weinanfdd26902022-03-01 14:18:25 -08002003constexpr char const* dumpManagerIface =
2004 "xyz.openbmc_project.Collection.DeleteAll";
Claire Weinandd72e872022-08-15 14:20:06 -07002005inline void handleBMCLogServicesCollectionGet(
Claire Weinanfdd26902022-03-01 14:18:25 -08002006 crow::App& app, const crow::Request& req,
2007 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2008{
2009 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2010 {
2011 return;
2012 }
2013 // Collections don't include the static data added by SubRoute
2014 // because it has a duplicate entry for members
2015 asyncResp->res.jsonValue["@odata.type"] =
2016 "#LogServiceCollection.LogServiceCollection";
2017 asyncResp->res.jsonValue["@odata.id"] =
2018 "/redfish/v1/Managers/bmc/LogServices";
2019 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2020 asyncResp->res.jsonValue["Description"] =
2021 "Collection of LogServices for this Manager";
2022 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2023 logServiceArray = nlohmann::json::array();
2024
2025#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
2026 logServiceArray.push_back(
2027 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
2028#endif
2029
2030 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2031
2032#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2033 auto respHandler =
2034 [asyncResp](
2035 const boost::system::error_code ec,
2036 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2037 if (ec)
2038 {
2039 BMCWEB_LOG_ERROR
Claire Weinandd72e872022-08-15 14:20:06 -07002040 << "handleBMCLogServicesCollectionGet respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -08002041 << ec;
2042 // Assume that getting an error simply means there are no dump
2043 // LogServices. Return without adding any error response.
2044 return;
2045 }
2046
2047 nlohmann::json& logServiceArrayLocal =
2048 asyncResp->res.jsonValue["Members"];
2049
2050 for (const std::string& path : subTreePaths)
2051 {
2052 if (path == "/xyz/openbmc_project/dump/bmc")
2053 {
2054 logServiceArrayLocal.push_back(
2055 {{"@odata.id",
2056 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
2057 }
2058 else if (path == "/xyz/openbmc_project/dump/faultlog")
2059 {
2060 logServiceArrayLocal.push_back(
2061 {{"@odata.id",
2062 "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
2063 }
2064 }
2065
2066 asyncResp->res.jsonValue["Members@odata.count"] =
2067 logServiceArrayLocal.size();
2068 };
2069
2070 crow::connections::systemBus->async_method_call(
2071 respHandler, "xyz.openbmc_project.ObjectMapper",
2072 "/xyz/openbmc_project/object_mapper",
2073 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2074 "/xyz/openbmc_project/dump", 0,
2075 std::array<const char*, 1>{dumpManagerIface});
2076#endif
2077}
2078
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002079inline void requestRoutesBMCLogServiceCollection(App& app)
2080{
2081 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002082 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002083 .methods(boost::beast::http::verb::get)(
Claire Weinandd72e872022-08-15 14:20:06 -07002084 std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002085}
Ed Tanous1da66f72018-07-27 16:13:37 -07002086
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002087inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002088{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002089 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002090 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002091 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002092 [&app](const crow::Request& req,
2093 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002094 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002095 {
2096 return;
2097 }
2098 asyncResp->res.jsonValue["@odata.type"] =
2099 "#LogService.v1_1_0.LogService";
2100 asyncResp->res.jsonValue["@odata.id"] =
2101 "/redfish/v1/Managers/bmc/LogServices/Journal";
2102 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2103 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2104 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2105 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302106
Ed Tanous002d39b2022-05-31 08:59:27 -07002107 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002108 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002109 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2110 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2111 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302112
Ed Tanous002d39b2022-05-31 08:59:27 -07002113 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2114 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2115 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002116}
Jason M. Billse1f26342018-07-18 12:12:00 -07002117
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002118static int
2119 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2120 sd_journal* journal,
2121 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002122{
2123 // Get the Log Entry contents
2124 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002125
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002126 std::string message;
2127 std::string_view syslogID;
2128 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2129 if (ret < 0)
2130 {
2131 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2132 << strerror(-ret);
2133 }
2134 if (!syslogID.empty())
2135 {
2136 message += std::string(syslogID) + ": ";
2137 }
2138
Ed Tanous39e77502019-03-04 17:35:53 -08002139 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002140 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002141 if (ret < 0)
2142 {
2143 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2144 return 1;
2145 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002146 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002147
2148 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002149 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002150 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002151 if (ret < 0)
2152 {
2153 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002154 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002155
2156 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002157 std::string entryTimeStr;
2158 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002159 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002160 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002161 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002162
2163 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07002164 bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
2165 bmcJournalLogEntryJson["@odata.id"] =
2166 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2167 bmcJournalLogEntryID;
2168 bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2169 bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2170 bmcJournalLogEntryJson["Message"] = std::move(message);
2171 bmcJournalLogEntryJson["EntryType"] = "Oem";
2172 bmcJournalLogEntryJson["Severity"] = severity <= 2 ? "Critical"
2173 : severity <= 4 ? "Warning"
2174 : "OK";
2175 bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2176 bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
Jason M. Billse1f26342018-07-18 12:12:00 -07002177 return 0;
2178}
2179
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002180inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002181{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002182 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002183 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002184 .methods(boost::beast::http::verb::get)(
2185 [&app](const crow::Request& req,
2186 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2187 query_param::QueryCapabilities capabilities = {
2188 .canDelegateTop = true,
2189 .canDelegateSkip = true,
2190 };
2191 query_param::Query delegatedQuery;
2192 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002193 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002194 {
2195 return;
2196 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07002197
2198 size_t skip = delegatedQuery.skip.value_or(0);
Jiaqing Zhao5143f7a2022-07-22 09:33:33 +08002199 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
Ed Tanous3648c8b2022-07-25 13:39:59 -07002200
Ed Tanous002d39b2022-05-31 08:59:27 -07002201 // Collections don't include the static data added by SubRoute
2202 // because it has a duplicate entry for members
2203 asyncResp->res.jsonValue["@odata.type"] =
2204 "#LogEntryCollection.LogEntryCollection";
2205 asyncResp->res.jsonValue["@odata.id"] =
2206 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2207 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2208 asyncResp->res.jsonValue["Description"] =
2209 "Collection of BMC Journal Entries";
2210 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2211 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002212
Ed Tanous002d39b2022-05-31 08:59:27 -07002213 // Go through the journal and use the timestamp to create a
2214 // unique ID for each entry
2215 sd_journal* journalTmp = nullptr;
2216 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2217 if (ret < 0)
2218 {
2219 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2220 messages::internalError(asyncResp->res);
2221 return;
2222 }
2223 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2224 journalTmp, sd_journal_close);
2225 journalTmp = nullptr;
2226 uint64_t entryCount = 0;
2227 // Reset the unique ID on the first entry
2228 bool firstEntry = true;
2229 SD_JOURNAL_FOREACH(journal.get())
2230 {
2231 entryCount++;
2232 // Handle paging using skip (number of entries to skip from
2233 // the start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07002234 if (entryCount <= skip || entryCount > skip + top)
George Liu0fda0f12021-11-16 10:06:17 +08002235 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002236 continue;
2237 }
2238
2239 std::string idStr;
2240 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2241 {
2242 continue;
2243 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002244 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002245
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002246 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002247 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2248 bmcJournalLogEntry) != 0)
2249 {
George Liu0fda0f12021-11-16 10:06:17 +08002250 messages::internalError(asyncResp->res);
2251 return;
2252 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002253 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002254 }
2255 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07002256 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07002257 {
2258 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2259 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07002260 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07002261 }
George Liu0fda0f12021-11-16 10:06:17 +08002262 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002263}
Jason M. Billse1f26342018-07-18 12:12:00 -07002264
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002265inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002266{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002267 BMCWEB_ROUTE(app,
2268 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002269 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002270 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002271 [&app](const crow::Request& req,
2272 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2273 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002274 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002275 {
2276 return;
2277 }
2278 // Convert the unique ID back to a timestamp to find the entry
2279 uint64_t ts = 0;
2280 uint64_t index = 0;
2281 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2282 {
2283 return;
2284 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002285
Ed Tanous002d39b2022-05-31 08:59:27 -07002286 sd_journal* journalTmp = nullptr;
2287 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2288 if (ret < 0)
2289 {
2290 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2291 messages::internalError(asyncResp->res);
2292 return;
2293 }
2294 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2295 journalTmp, sd_journal_close);
2296 journalTmp = nullptr;
2297 // Go to the timestamp in the log and move to the entry at the
2298 // index tracking the unique ID
2299 std::string idStr;
2300 bool firstEntry = true;
2301 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2302 if (ret < 0)
2303 {
2304 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2305 << strerror(-ret);
2306 messages::internalError(asyncResp->res);
2307 return;
2308 }
2309 for (uint64_t i = 0; i <= index; i++)
2310 {
2311 sd_journal_next(journal.get());
2312 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2313 {
2314 messages::internalError(asyncResp->res);
2315 return;
2316 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002317 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002318 }
2319 // Confirm that the entry ID matches what was requested
2320 if (idStr != entryID)
2321 {
2322 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2323 return;
2324 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002325
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002326 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002327 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002328 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002329 {
2330 messages::internalError(asyncResp->res);
2331 return;
2332 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07002333 asyncResp->res.jsonValue.update(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002334 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002335}
2336
Claire Weinanfdd26902022-03-01 14:18:25 -08002337inline void
2338 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2339 const std::string& dumpType)
2340{
2341 std::string dumpPath;
2342 std::string overWritePolicy;
2343 bool collectDiagnosticDataSupported = false;
2344
2345 if (dumpType == "BMC")
2346 {
2347 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2348 overWritePolicy = "WrapsWhenFull";
2349 collectDiagnosticDataSupported = true;
2350 }
2351 else if (dumpType == "FaultLog")
2352 {
2353 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2354 overWritePolicy = "Unknown";
2355 collectDiagnosticDataSupported = false;
2356 }
2357 else if (dumpType == "System")
2358 {
2359 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2360 overWritePolicy = "WrapsWhenFull";
2361 collectDiagnosticDataSupported = true;
2362 }
2363 else
2364 {
2365 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2366 << dumpType;
2367 messages::internalError(asyncResp->res);
2368 return;
2369 }
2370
2371 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2372 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2373 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2374 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2375 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2376 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2377
2378 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002379 redfish::time_utils::getDateTimeOffsetNow();
Claire Weinanfdd26902022-03-01 14:18:25 -08002380 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2381 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2382 redfishDateTimeOffset.second;
2383
2384 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2385 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2386 dumpPath + "/Actions/LogService.ClearLog";
2387
2388 if (collectDiagnosticDataSupported)
2389 {
2390 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2391 ["target"] =
2392 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2393 }
2394}
2395
2396inline void handleLogServicesDumpServiceGet(
2397 crow::App& app, const std::string& dumpType, const crow::Request& req,
2398 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2399{
2400 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2401 {
2402 return;
2403 }
2404 getDumpServiceInfo(asyncResp, dumpType);
2405}
2406
2407inline void handleLogServicesDumpEntriesCollectionGet(
2408 crow::App& app, const std::string& dumpType, const crow::Request& req,
2409 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2410{
2411 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2412 {
2413 return;
2414 }
2415 getDumpEntryCollection(asyncResp, dumpType);
2416}
2417
2418inline void handleLogServicesDumpEntryGet(
2419 crow::App& app, const std::string& dumpType, const crow::Request& req,
2420 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2421 const std::string& dumpId)
2422{
2423 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2424 {
2425 return;
2426 }
2427 getDumpEntryById(asyncResp, dumpId, dumpType);
2428}
2429
2430inline void handleLogServicesDumpEntryDelete(
2431 crow::App& app, const std::string& dumpType, const crow::Request& req,
2432 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2433 const std::string& dumpId)
2434{
2435 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2436 {
2437 return;
2438 }
2439 deleteDumpEntry(asyncResp, dumpId, dumpType);
2440}
2441
2442inline void handleLogServicesDumpCollectDiagnosticDataPost(
2443 crow::App& app, const std::string& dumpType, const crow::Request& req,
2444 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2445{
2446 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2447 {
2448 return;
2449 }
2450 createDump(asyncResp, req, dumpType);
2451}
2452
2453inline void handleLogServicesDumpClearLogPost(
2454 crow::App& app, const std::string& dumpType, const crow::Request& req,
2455 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2456{
2457 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2458 {
2459 return;
2460 }
2461 clearDump(asyncResp, dumpType);
2462}
2463
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002464inline void requestRoutesBMCDumpService(App& app)
2465{
2466 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002467 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002468 .methods(boost::beast::http::verb::get)(std::bind_front(
2469 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002470}
2471
2472inline void requestRoutesBMCDumpEntryCollection(App& app)
2473{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002474 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002475 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002476 .methods(boost::beast::http::verb::get)(std::bind_front(
2477 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002478}
2479
2480inline void requestRoutesBMCDumpEntry(App& app)
2481{
2482 BMCWEB_ROUTE(app,
2483 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002484 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002485 .methods(boost::beast::http::verb::get)(std::bind_front(
2486 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2487
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002488 BMCWEB_ROUTE(app,
2489 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002490 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002491 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2492 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002493}
2494
2495inline void requestRoutesBMCDumpCreate(App& app)
2496{
George Liu0fda0f12021-11-16 10:06:17 +08002497 BMCWEB_ROUTE(
2498 app,
2499 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002500 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002501 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002502 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2503 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002504}
2505
2506inline void requestRoutesBMCDumpClear(App& app)
2507{
George Liu0fda0f12021-11-16 10:06:17 +08002508 BMCWEB_ROUTE(
2509 app,
2510 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002511 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002512 .methods(boost::beast::http::verb::post)(std::bind_front(
2513 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2514}
2515
2516inline void requestRoutesFaultLogDumpService(App& app)
2517{
2518 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2519 .privileges(redfish::privileges::getLogService)
2520 .methods(boost::beast::http::verb::get)(std::bind_front(
2521 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2522}
2523
2524inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2525{
2526 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2527 .privileges(redfish::privileges::getLogEntryCollection)
2528 .methods(boost::beast::http::verb::get)(
2529 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2530 std::ref(app), "FaultLog"));
2531}
2532
2533inline void requestRoutesFaultLogDumpEntry(App& app)
2534{
2535 BMCWEB_ROUTE(app,
2536 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2537 .privileges(redfish::privileges::getLogEntry)
2538 .methods(boost::beast::http::verb::get)(std::bind_front(
2539 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2540
2541 BMCWEB_ROUTE(app,
2542 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2543 .privileges(redfish::privileges::deleteLogEntry)
2544 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2545 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2546}
2547
2548inline void requestRoutesFaultLogDumpClear(App& app)
2549{
2550 BMCWEB_ROUTE(
2551 app,
2552 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2553 .privileges(redfish::privileges::postLogService)
2554 .methods(boost::beast::http::verb::post)(std::bind_front(
2555 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002556}
2557
2558inline void requestRoutesSystemDumpService(App& app)
2559{
2560 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002561 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002562 .methods(boost::beast::http::verb::get)(
2563 [&app](const crow::Request& req,
2564 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002565 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002566 {
2567 return;
2568 }
2569 asyncResp->res.jsonValue["@odata.id"] =
2570 "/redfish/v1/Systems/system/LogServices/Dump";
2571 asyncResp->res.jsonValue["@odata.type"] =
2572 "#LogService.v1_2_0.LogService";
2573 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2574 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2575 asyncResp->res.jsonValue["Id"] = "Dump";
2576 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302577
Ed Tanous002d39b2022-05-31 08:59:27 -07002578 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002579 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002580 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2581 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2582 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302583
Ed Tanous002d39b2022-05-31 08:59:27 -07002584 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2585 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2586 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2587 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002588
Ed Tanous002d39b2022-05-31 08:59:27 -07002589 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2590 ["target"] =
2591 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002592 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002593}
2594
2595inline void requestRoutesSystemDumpEntryCollection(App& app)
2596{
2597
2598 /**
2599 * Functions triggers appropriate requests on DBus
2600 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002601 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002602 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002603 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002604 [&app](const crow::Request& req,
2605 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002606 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002607 {
2608 return;
2609 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002610 getDumpEntryCollection(asyncResp, "System");
2611 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002612}
2613
2614inline void requestRoutesSystemDumpEntry(App& app)
2615{
2616 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002617 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002618 .privileges(redfish::privileges::getLogEntry)
2619
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002620 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002621 [&app](const crow::Request& req,
2622 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2623 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002624 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002625 {
2626 return;
2627 }
2628 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002629 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002630
2631 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002632 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002633 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002634 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002635 [&app](const crow::Request& req,
2636 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2637 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002638 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002639 {
2640 return;
2641 }
2642 deleteDumpEntry(asyncResp, param, "system");
2643 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002644}
2645
2646inline void requestRoutesSystemDumpCreate(App& app)
2647{
George Liu0fda0f12021-11-16 10:06:17 +08002648 BMCWEB_ROUTE(
2649 app,
2650 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002651 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002652 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002653 [&app](const crow::Request& req,
2654 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002655 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002656 {
2657 return;
2658 }
2659 createDump(asyncResp, req, "System");
2660 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002661}
2662
2663inline void requestRoutesSystemDumpClear(App& app)
2664{
George Liu0fda0f12021-11-16 10:06:17 +08002665 BMCWEB_ROUTE(
2666 app,
2667 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002668 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002669 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002670 [&app](const crow::Request& req,
2671 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002672
Ed Tanous45ca1b82022-03-25 13:07:27 -07002673 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002674 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002675 {
2676 return;
2677 }
2678 clearDump(asyncResp, "System");
2679 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002680}
2681
2682inline void requestRoutesCrashdumpService(App& app)
2683{
2684 // Note: Deviated from redfish privilege registry for GET & HEAD
2685 // method for security reasons.
2686 /**
2687 * Functions triggers appropriate requests on DBus
2688 */
2689 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002690 // This is incorrect, should be:
2691 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002692 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002693 .methods(boost::beast::http::verb::get)(
2694 [&app](const crow::Request& req,
2695 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002696 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002697 {
2698 return;
2699 }
2700 // Copy over the static data to include the entries added by
2701 // SubRoute
2702 asyncResp->res.jsonValue["@odata.id"] =
2703 "/redfish/v1/Systems/system/LogServices/Crashdump";
2704 asyncResp->res.jsonValue["@odata.type"] =
2705 "#LogService.v1_2_0.LogService";
2706 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2707 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2708 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2709 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2710 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302711
Ed Tanous002d39b2022-05-31 08:59:27 -07002712 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002713 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002714 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2715 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2716 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302717
Ed Tanous002d39b2022-05-31 08:59:27 -07002718 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2719 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2720 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2721 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2722 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2723 ["target"] =
2724 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002725 });
2726}
2727
2728void inline requestRoutesCrashdumpClear(App& app)
2729{
George Liu0fda0f12021-11-16 10:06:17 +08002730 BMCWEB_ROUTE(
2731 app,
2732 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002733 // This is incorrect, should be:
2734 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002735 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002736 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002737 [&app](const crow::Request& req,
2738 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002739 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002740 {
2741 return;
2742 }
2743 crow::connections::systemBus->async_method_call(
2744 [asyncResp](const boost::system::error_code ec,
2745 const std::string&) {
2746 if (ec)
2747 {
2748 messages::internalError(asyncResp->res);
2749 return;
2750 }
2751 messages::success(asyncResp->res);
2752 },
2753 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2754 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002755}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002756
zhanghch058d1b46d2021-04-01 11:18:24 +08002757static void
2758 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2759 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002760{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002761 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002762 [asyncResp, logID,
2763 &logEntryJson](const boost::system::error_code ec,
2764 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002765 if (ec)
2766 {
2767 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2768 if (ec.value() ==
2769 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002770 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002771 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002772 }
2773 else
2774 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002775 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002776 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002777 return;
2778 }
2779
2780 std::string timestamp{};
2781 std::string filename{};
2782 std::string logfile{};
2783 parseCrashdumpParameters(params, filename, timestamp, logfile);
2784
2785 if (filename.empty() || timestamp.empty())
2786 {
2787 messages::resourceMissingAtURI(asyncResp->res,
2788 crow::utility::urlFromPieces(logID));
2789 return;
2790 }
2791
2792 std::string crashdumpURI =
2793 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2794 logID + "/" + filename;
Jason M. Bills84afc482022-06-24 12:38:23 -07002795 nlohmann::json::object_t logEntry;
2796 logEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
2797 logEntry["@odata.id"] =
2798 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
2799 logEntry["Name"] = "CPU Crashdump";
2800 logEntry["Id"] = logID;
2801 logEntry["EntryType"] = "Oem";
2802 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2803 logEntry["DiagnosticDataType"] = "OEM";
2804 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2805 logEntry["Created"] = std::move(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07002806
2807 // If logEntryJson references an array of LogEntry resources
2808 // ('Members' list), then push this as a new entry, otherwise set it
2809 // directly
2810 if (logEntryJson.is_array())
2811 {
2812 logEntryJson.push_back(logEntry);
2813 asyncResp->res.jsonValue["Members@odata.count"] =
2814 logEntryJson.size();
2815 }
2816 else
2817 {
Jason M. Billsd405bb52022-06-24 10:52:05 -07002818 logEntryJson.update(logEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002819 }
2820 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002821 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002822 std::move(getStoredLogCallback), crashdumpObject,
2823 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002824 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002825}
2826
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002827inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002828{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002829 // Note: Deviated from redfish privilege registry for GET & HEAD
2830 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002831 /**
2832 * Functions triggers appropriate requests on DBus
2833 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002834 BMCWEB_ROUTE(app,
2835 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002836 // This is incorrect, should be.
2837 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002838 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002839 .methods(boost::beast::http::verb::get)(
2840 [&app](const crow::Request& req,
2841 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002842 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002843 {
2844 return;
2845 }
2846 crow::connections::systemBus->async_method_call(
2847 [asyncResp](const boost::system::error_code ec,
2848 const std::vector<std::string>& resp) {
2849 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002850 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002851 if (ec.value() !=
2852 boost::system::errc::no_such_file_or_directory)
2853 {
2854 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2855 << ec.message();
2856 messages::internalError(asyncResp->res);
2857 return;
2858 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002859 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002860 asyncResp->res.jsonValue["@odata.type"] =
2861 "#LogEntryCollection.LogEntryCollection";
2862 asyncResp->res.jsonValue["@odata.id"] =
2863 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2864 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2865 asyncResp->res.jsonValue["Description"] =
2866 "Collection of Crashdump Entries";
2867 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2868 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002869
Ed Tanous002d39b2022-05-31 08:59:27 -07002870 for (const std::string& path : resp)
2871 {
2872 const sdbusplus::message::object_path objPath(path);
2873 // Get the log ID
2874 std::string logID = objPath.filename();
2875 if (logID.empty())
2876 {
2877 continue;
2878 }
2879 // Add the log entry to the array
2880 logCrashdumpEntry(asyncResp, logID,
2881 asyncResp->res.jsonValue["Members"]);
2882 }
2883 },
2884 "xyz.openbmc_project.ObjectMapper",
2885 "/xyz/openbmc_project/object_mapper",
2886 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2887 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002888 });
2889}
Ed Tanous1da66f72018-07-27 16:13:37 -07002890
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002891inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002892{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002893 // Note: Deviated from redfish privilege registry for GET & HEAD
2894 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002895
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002896 BMCWEB_ROUTE(
2897 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002898 // this is incorrect, should be
2899 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002900 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002901 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002902 [&app](const crow::Request& req,
2903 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2904 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002905 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002906 {
2907 return;
2908 }
2909 const std::string& logID = param;
2910 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2911 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002912}
Ed Tanous1da66f72018-07-27 16:13:37 -07002913
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002914inline void requestRoutesCrashdumpFile(App& app)
2915{
2916 // Note: Deviated from redfish privilege registry for GET & HEAD
2917 // method for security reasons.
2918 BMCWEB_ROUTE(
2919 app,
2920 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002921 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002922 .methods(boost::beast::http::verb::get)(
Nan Zhoua4ce1142022-08-02 18:45:25 +00002923 [](const crow::Request& req,
2924 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2925 const std::string& logID, const std::string& fileName) {
Shounak Mitra2a9beee2022-07-20 18:41:30 +00002926 // Do not call getRedfishRoute here since the crashdump file is not a
2927 // Redfish resource.
Ed Tanous002d39b2022-05-31 08:59:27 -07002928 auto getStoredLogCallback =
2929 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2930 const boost::system::error_code ec,
2931 const std::vector<
2932 std::pair<std::string, dbus::utility::DbusVariantType>>&
2933 resp) {
2934 if (ec)
2935 {
2936 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2937 messages::internalError(asyncResp->res);
2938 return;
2939 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002940
Ed Tanous002d39b2022-05-31 08:59:27 -07002941 std::string dbusFilename{};
2942 std::string dbusTimestamp{};
2943 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002944
Ed Tanous002d39b2022-05-31 08:59:27 -07002945 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2946 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002947
Ed Tanous002d39b2022-05-31 08:59:27 -07002948 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2949 dbusFilepath.empty())
2950 {
2951 messages::resourceMissingAtURI(asyncResp->res, url);
2952 return;
2953 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002954
Ed Tanous002d39b2022-05-31 08:59:27 -07002955 // Verify the file name parameter is correct
2956 if (fileName != dbusFilename)
2957 {
2958 messages::resourceMissingAtURI(asyncResp->res, url);
2959 return;
2960 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002961
Ed Tanous002d39b2022-05-31 08:59:27 -07002962 if (!std::filesystem::exists(dbusFilepath))
2963 {
2964 messages::resourceMissingAtURI(asyncResp->res, url);
2965 return;
2966 }
2967 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2968 asyncResp->res.body() =
2969 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002970
Ed Tanous002d39b2022-05-31 08:59:27 -07002971 // Configure this to be a file download when accessed
2972 // from a browser
Ed Tanousd9f6c622022-03-17 09:12:17 -07002973 asyncResp->res.addHeader(
2974 boost::beast::http::field::content_disposition, "attachment");
Ed Tanous002d39b2022-05-31 08:59:27 -07002975 };
2976 crow::connections::systemBus->async_method_call(
2977 std::move(getStoredLogCallback), crashdumpObject,
2978 crashdumpPath + std::string("/") + logID,
2979 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2980 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002981}
2982
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002983enum class OEMDiagnosticType
2984{
2985 onDemand,
2986 telemetry,
2987 invalid,
2988};
2989
Ed Tanousf7725d72022-03-07 12:46:00 -08002990inline OEMDiagnosticType
2991 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002992{
2993 if (oemDiagStr == "OnDemand")
2994 {
2995 return OEMDiagnosticType::onDemand;
2996 }
2997 if (oemDiagStr == "Telemetry")
2998 {
2999 return OEMDiagnosticType::telemetry;
3000 }
3001
3002 return OEMDiagnosticType::invalid;
3003}
3004
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003005inline void requestRoutesCrashdumpCollect(App& app)
3006{
3007 // Note: Deviated from redfish privilege registry for GET & HEAD
3008 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08003009 BMCWEB_ROUTE(
3010 app,
3011 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07003012 // The below is incorrect; Should be ConfigureManager
3013 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003014 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07003015 .methods(boost::beast::http::verb::post)(
3016 [&app](const crow::Request& req,
3017 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003018 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003019 {
3020 return;
3021 }
3022 std::string diagnosticDataType;
3023 std::string oemDiagnosticDataType;
3024 if (!redfish::json_util::readJsonAction(
3025 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3026 "OEMDiagnosticDataType", oemDiagnosticDataType))
3027 {
3028 return;
3029 }
3030
3031 if (diagnosticDataType != "OEM")
3032 {
3033 BMCWEB_LOG_ERROR
3034 << "Only OEM DiagnosticDataType supported for Crashdump";
3035 messages::actionParameterValueFormatError(
3036 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3037 "CollectDiagnosticData");
3038 return;
3039 }
3040
3041 OEMDiagnosticType oemDiagType =
3042 getOEMDiagnosticType(oemDiagnosticDataType);
3043
3044 std::string iface;
3045 std::string method;
3046 std::string taskMatchStr;
3047 if (oemDiagType == OEMDiagnosticType::onDemand)
3048 {
3049 iface = crashdumpOnDemandInterface;
3050 method = "GenerateOnDemandLog";
3051 taskMatchStr = "type='signal',"
3052 "interface='org.freedesktop.DBus.Properties',"
3053 "member='PropertiesChanged',"
3054 "arg0namespace='com.intel.crashdump'";
3055 }
3056 else if (oemDiagType == OEMDiagnosticType::telemetry)
3057 {
3058 iface = crashdumpTelemetryInterface;
3059 method = "GenerateTelemetryLog";
3060 taskMatchStr = "type='signal',"
3061 "interface='org.freedesktop.DBus.Properties',"
3062 "member='PropertiesChanged',"
3063 "arg0namespace='com.intel.crashdump'";
3064 }
3065 else
3066 {
3067 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3068 << oemDiagnosticDataType;
3069 messages::actionParameterValueFormatError(
3070 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3071 "CollectDiagnosticData");
3072 return;
3073 }
3074
3075 auto collectCrashdumpCallback =
3076 [asyncResp, payload(task::Payload(req)),
3077 taskMatchStr](const boost::system::error_code ec,
3078 const std::string&) mutable {
3079 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003080 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003081 if (ec.value() == boost::system::errc::operation_not_supported)
3082 {
3083 messages::resourceInStandby(asyncResp->res);
3084 }
3085 else if (ec.value() ==
3086 boost::system::errc::device_or_resource_busy)
3087 {
3088 messages::serviceTemporarilyUnavailable(asyncResp->res,
3089 "60");
3090 }
3091 else
3092 {
3093 messages::internalError(asyncResp->res);
3094 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003095 return;
3096 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003097 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -05003098 [](boost::system::error_code err, sdbusplus::message_t&,
Ed Tanous002d39b2022-05-31 08:59:27 -07003099 const std::shared_ptr<task::TaskData>& taskData) {
3100 if (!err)
3101 {
3102 taskData->messages.emplace_back(messages::taskCompletedOK(
3103 std::to_string(taskData->index)));
3104 taskData->state = "Completed";
3105 }
3106 return task::completed;
3107 },
3108 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003109
Ed Tanous002d39b2022-05-31 08:59:27 -07003110 task->startTimer(std::chrono::minutes(5));
3111 task->populateResp(asyncResp->res);
3112 task->payload.emplace(std::move(payload));
3113 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003114
Ed Tanous002d39b2022-05-31 08:59:27 -07003115 crow::connections::systemBus->async_method_call(
3116 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3117 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003118 });
3119}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003120
Andrew Geisslercb92c032018-08-17 07:56:14 -07003121/**
3122 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3123 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003124inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003125{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003126 /**
3127 * Function handles POST method request.
3128 * The Clear Log actions does not require any parameter.The action deletes
3129 * all entries found in the Entries collection for this Log Service.
3130 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003131
George Liu0fda0f12021-11-16 10:06:17 +08003132 BMCWEB_ROUTE(
3133 app,
3134 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003135 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003136 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003137 [&app](const crow::Request& req,
3138 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003139 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003140 {
3141 return;
3142 }
3143 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003144
Ed Tanous002d39b2022-05-31 08:59:27 -07003145 // Process response from Logging service.
3146 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3147 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3148 if (ec)
3149 {
3150 // TODO Handle for specific error code
3151 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3152 asyncResp->res.result(
3153 boost::beast::http::status::internal_server_error);
3154 return;
3155 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003156
Ed Tanous002d39b2022-05-31 08:59:27 -07003157 asyncResp->res.result(boost::beast::http::status::no_content);
3158 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003159
Ed Tanous002d39b2022-05-31 08:59:27 -07003160 // Make call to Logging service to request Clear Log
3161 crow::connections::systemBus->async_method_call(
3162 respHandler, "xyz.openbmc_project.Logging",
3163 "/xyz/openbmc_project/logging",
3164 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3165 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003166}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003167
3168/****************************************************
3169 * Redfish PostCode interfaces
3170 * using DBUS interface: getPostCodesTS
3171 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003172inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003173{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003174 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003175 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003176 .methods(boost::beast::http::verb::get)(
3177 [&app](const crow::Request& req,
3178 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003179 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003180 {
3181 return;
3182 }
Ed Tanous14766872022-03-15 10:44:42 -07003183
Ed Tanous002d39b2022-05-31 08:59:27 -07003184 asyncResp->res.jsonValue["@odata.id"] =
3185 "/redfish/v1/Systems/system/LogServices/PostCodes";
3186 asyncResp->res.jsonValue["@odata.type"] =
3187 "#LogService.v1_1_0.LogService";
3188 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3189 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3190 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3191 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3192 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3193 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303194
Ed Tanous002d39b2022-05-31 08:59:27 -07003195 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07003196 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07003197 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3198 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3199 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303200
Ed Tanous002d39b2022-05-31 08:59:27 -07003201 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3202 {"target",
3203 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003204 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003205}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003206
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003207inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003208{
George Liu0fda0f12021-11-16 10:06:17 +08003209 BMCWEB_ROUTE(
3210 app,
3211 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003212 // The following privilege is incorrect; It should be ConfigureManager
3213 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003214 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003215 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003216 [&app](const crow::Request& req,
3217 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003218 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003219 {
3220 return;
3221 }
3222 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003223
Ed Tanous002d39b2022-05-31 08:59:27 -07003224 // Make call to post-code service to request clear all
3225 crow::connections::systemBus->async_method_call(
3226 [asyncResp](const boost::system::error_code ec) {
3227 if (ec)
3228 {
3229 // TODO Handle for specific error code
3230 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3231 << ec;
3232 asyncResp->res.result(
3233 boost::beast::http::status::internal_server_error);
3234 messages::internalError(asyncResp->res);
3235 return;
3236 }
3237 },
3238 "xyz.openbmc_project.State.Boot.PostCode0",
3239 "/xyz/openbmc_project/State/Boot/PostCode0",
3240 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3241 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003242}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003243
3244static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003245 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303246 const boost::container::flat_map<
3247 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003248 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3249 const uint64_t skip = 0, const uint64_t top = 0)
3250{
3251 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003252 const registries::Message* message =
3253 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003254
3255 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003256 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003257
3258 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303259 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3260 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003261 {
3262 currentCodeIndex++;
3263 std::string postcodeEntryID =
3264 "B" + std::to_string(bootIndex) + "-" +
3265 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3266
3267 uint64_t usecSinceEpoch = code.first;
3268 uint64_t usTimeOffset = 0;
3269
3270 if (1 == currentCodeIndex)
3271 { // already incremented
3272 firstCodeTimeUs = code.first;
3273 }
3274 else
3275 {
3276 usTimeOffset = code.first - firstCodeTimeUs;
3277 }
3278
3279 // skip if no specific codeIndex is specified and currentCodeIndex does
3280 // not fall between top and skip
3281 if ((codeIndex == 0) &&
3282 (currentCodeIndex <= skip || currentCodeIndex > top))
3283 {
3284 continue;
3285 }
3286
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003287 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003288 // currentIndex
3289 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3290 {
3291 // This is done for simplicity. 1st entry is needed to calculate
3292 // time offset. To improve efficiency, one can get to the entry
3293 // directly (possibly with flatmap's nth method)
3294 continue;
3295 }
3296
3297 // currentCodeIndex is within top and skip or equal to specified code
3298 // index
3299
3300 // Get the Created time from the timestamp
3301 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003302 entryTimeStr =
Ed Tanous2b829372022-08-03 14:22:34 -07003303 redfish::time_utils::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003304
3305 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3306 std::ostringstream hexCode;
3307 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303308 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003309 std::ostringstream timeOffsetStr;
3310 // Set Fixed -Point Notation
3311 timeOffsetStr << std::fixed;
3312 // Set precision to 4 digits
3313 timeOffsetStr << std::setprecision(4);
3314 // Add double to stream
3315 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3316 std::vector<std::string> messageArgs = {
3317 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3318
3319 // Get MessageArgs template from message registry
3320 std::string msg;
3321 if (message != nullptr)
3322 {
3323 msg = message->message;
3324
3325 // fill in this post code value
3326 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003327 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003328 {
3329 std::string argStr = "%" + std::to_string(++i);
3330 size_t argPos = msg.find(argStr);
3331 if (argPos != std::string::npos)
3332 {
3333 msg.replace(argPos, argStr.length(), messageArg);
3334 }
3335 }
3336 }
3337
Tim Leed4342a92020-04-27 11:47:58 +08003338 // Get Severity template from message registry
3339 std::string severity;
3340 if (message != nullptr)
3341 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003342 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003343 }
3344
ZhikuiRena3316fc2020-01-29 14:58:08 -08003345 // add to AsyncResp
3346 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003347 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills84afc482022-06-24 12:38:23 -07003348 bmcLogEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
3349 bmcLogEntry["@odata.id"] =
3350 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3351 postcodeEntryID;
3352 bmcLogEntry["Name"] = "POST Code Log Entry";
3353 bmcLogEntry["Id"] = postcodeEntryID;
3354 bmcLogEntry["Message"] = std::move(msg);
3355 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3356 bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3357 bmcLogEntry["EntryType"] = "Event";
3358 bmcLogEntry["Severity"] = std::move(severity);
3359 bmcLogEntry["Created"] = entryTimeStr;
George Liu647b3cd2021-07-05 12:43:56 +08003360 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3361 {
3362 bmcLogEntry["AdditionalDataURI"] =
3363 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3364 postcodeEntryID + "/attachment";
3365 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003366 }
3367}
3368
zhanghch058d1b46d2021-04-01 11:18:24 +08003369static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003370 const uint16_t bootIndex,
3371 const uint64_t codeIndex)
3372{
3373 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303374 [aResp, bootIndex,
3375 codeIndex](const boost::system::error_code ec,
3376 const boost::container::flat_map<
3377 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3378 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003379 if (ec)
3380 {
3381 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3382 messages::internalError(aResp->res);
3383 return;
3384 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003385
Ed Tanous002d39b2022-05-31 08:59:27 -07003386 // skip the empty postcode boots
3387 if (postcode.empty())
3388 {
3389 return;
3390 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003391
Ed Tanous002d39b2022-05-31 08:59:27 -07003392 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003393
Ed Tanous002d39b2022-05-31 08:59:27 -07003394 aResp->res.jsonValue["Members@odata.count"] =
3395 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003396 },
Jonathan Doman15124762021-01-07 17:54:17 -08003397 "xyz.openbmc_project.State.Boot.PostCode0",
3398 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003399 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3400 bootIndex);
3401}
3402
zhanghch058d1b46d2021-04-01 11:18:24 +08003403static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003404 const uint16_t bootIndex,
3405 const uint16_t bootCount,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003406 const uint64_t entryCount, size_t skip,
3407 size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003408{
3409 crow::connections::systemBus->async_method_call(
3410 [aResp, bootIndex, bootCount, entryCount, skip,
3411 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303412 const boost::container::flat_map<
3413 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3414 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003415 if (ec)
3416 {
3417 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3418 messages::internalError(aResp->res);
3419 return;
3420 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003421
Ed Tanous002d39b2022-05-31 08:59:27 -07003422 uint64_t endCount = entryCount;
3423 if (!postcode.empty())
3424 {
3425 endCount = entryCount + postcode.size();
Ed Tanous3648c8b2022-07-25 13:39:59 -07003426 if (skip < endCount && (top + skip) > entryCount)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003427 {
Ed Tanous3648c8b2022-07-25 13:39:59 -07003428 uint64_t thisBootSkip =
3429 std::max(static_cast<uint64_t>(skip), entryCount) -
3430 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003431 uint64_t thisBootTop =
Ed Tanous3648c8b2022-07-25 13:39:59 -07003432 std::min(static_cast<uint64_t>(top + skip), endCount) -
3433 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003434
3435 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3436 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003437 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003438 aResp->res.jsonValue["Members@odata.count"] = endCount;
3439 }
3440
3441 // continue to previous bootIndex
3442 if (bootIndex < bootCount)
3443 {
3444 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3445 bootCount, endCount, skip, top);
3446 }
Jiaqing Zhao81584ab2022-07-28 00:33:45 +08003447 else if (skip + top < endCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07003448 {
3449 aResp->res.jsonValue["Members@odata.nextLink"] =
3450 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3451 std::to_string(skip + top);
3452 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003453 },
Jonathan Doman15124762021-01-07 17:54:17 -08003454 "xyz.openbmc_project.State.Boot.PostCode0",
3455 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003456 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3457 bootIndex);
3458}
3459
zhanghch058d1b46d2021-04-01 11:18:24 +08003460static void
3461 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003462 size_t skip, size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003463{
3464 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003465 sdbusplus::asio::getProperty<uint16_t>(
3466 *crow::connections::systemBus,
3467 "xyz.openbmc_project.State.Boot.PostCode0",
3468 "/xyz/openbmc_project/State/Boot/PostCode0",
3469 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3470 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3471 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003472 if (ec)
3473 {
3474 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3475 messages::internalError(aResp->res);
3476 return;
3477 }
3478 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003479 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003480}
3481
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003482inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003483{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003484 BMCWEB_ROUTE(app,
3485 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003486 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003487 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003488 [&app](const crow::Request& req,
3489 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003490 query_param::QueryCapabilities capabilities = {
3491 .canDelegateTop = true,
3492 .canDelegateSkip = true,
3493 };
3494 query_param::Query delegatedQuery;
3495 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003496 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003497 {
3498 return;
3499 }
3500 asyncResp->res.jsonValue["@odata.type"] =
3501 "#LogEntryCollection.LogEntryCollection";
3502 asyncResp->res.jsonValue["@odata.id"] =
3503 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3504 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3505 asyncResp->res.jsonValue["Description"] =
3506 "Collection of POST Code Log Entries";
3507 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3508 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07003509 size_t skip = delegatedQuery.skip.value_or(0);
Jiaqing Zhao5143f7a2022-07-22 09:33:33 +08003510 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
Ed Tanous3648c8b2022-07-25 13:39:59 -07003511 getCurrentBootNumber(asyncResp, skip, top);
Ed Tanous002d39b2022-05-31 08:59:27 -07003512 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003513}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003514
George Liu647b3cd2021-07-05 12:43:56 +08003515/**
3516 * @brief Parse post code ID and get the current value and index value
3517 * eg: postCodeID=B1-2, currentValue=1, index=2
3518 *
3519 * @param[in] postCodeID Post Code ID
3520 * @param[out] currentValue Current value
3521 * @param[out] index Index value
3522 *
3523 * @return bool true if the parsing is successful, false the parsing fails
3524 */
3525inline static bool parsePostCode(const std::string& postCodeID,
3526 uint64_t& currentValue, uint16_t& index)
3527{
3528 std::vector<std::string> split;
3529 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3530 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3531 {
3532 return false;
3533 }
3534
Ed Tanousca45aa32022-01-07 09:28:45 -08003535 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003536 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003537 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003538 const char* end = split[0].data() + split[0].size();
3539 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3540
3541 if (ptrIndex != end || ecIndex != std::errc())
3542 {
3543 return false;
3544 }
3545
3546 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003547
3548 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003549 end = split[1].data() + split[1].size();
3550 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003551
Tony Lee517d9a52022-06-28 15:41:23 +08003552 return ptrValue == end && ecValue == std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003553}
3554
3555inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3556{
George Liu0fda0f12021-11-16 10:06:17 +08003557 BMCWEB_ROUTE(
3558 app,
3559 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003560 .privileges(redfish::privileges::getLogEntry)
3561 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003562 [&app](const crow::Request& req,
3563 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3564 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003565 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003566 {
3567 return;
3568 }
3569 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3570 {
3571 asyncResp->res.result(boost::beast::http::status::bad_request);
3572 return;
3573 }
George Liu647b3cd2021-07-05 12:43:56 +08003574
Ed Tanous002d39b2022-05-31 08:59:27 -07003575 uint64_t currentValue = 0;
3576 uint16_t index = 0;
3577 if (!parsePostCode(postCodeID, currentValue, index))
3578 {
3579 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3580 return;
3581 }
George Liu647b3cd2021-07-05 12:43:56 +08003582
Ed Tanous002d39b2022-05-31 08:59:27 -07003583 crow::connections::systemBus->async_method_call(
3584 [asyncResp, postCodeID, currentValue](
3585 const boost::system::error_code ec,
3586 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3587 postcodes) {
3588 if (ec.value() == EBADR)
3589 {
3590 messages::resourceNotFound(asyncResp->res, "LogEntry",
3591 postCodeID);
3592 return;
3593 }
3594 if (ec)
3595 {
3596 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3597 messages::internalError(asyncResp->res);
3598 return;
3599 }
George Liu647b3cd2021-07-05 12:43:56 +08003600
Ed Tanous002d39b2022-05-31 08:59:27 -07003601 size_t value = static_cast<size_t>(currentValue) - 1;
3602 if (value == std::string::npos || postcodes.size() < currentValue)
3603 {
3604 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3605 messages::resourceNotFound(asyncResp->res, "LogEntry",
3606 postCodeID);
3607 return;
3608 }
George Liu647b3cd2021-07-05 12:43:56 +08003609
Ed Tanous002d39b2022-05-31 08:59:27 -07003610 const auto& [tID, c] = postcodes[value];
3611 if (c.empty())
3612 {
3613 BMCWEB_LOG_INFO << "No found post code data";
3614 messages::resourceNotFound(asyncResp->res, "LogEntry",
3615 postCodeID);
3616 return;
3617 }
3618 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3619 const char* d = reinterpret_cast<const char*>(c.data());
3620 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003621
Ed Tanousd9f6c622022-03-17 09:12:17 -07003622 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07003623 "application/octet-stream");
Ed Tanousd9f6c622022-03-17 09:12:17 -07003624 asyncResp->res.addHeader(
3625 boost::beast::http::field::content_transfer_encoding, "Base64");
Ed Tanous002d39b2022-05-31 08:59:27 -07003626 asyncResp->res.body() = crow::utility::base64encode(strData);
3627 },
3628 "xyz.openbmc_project.State.Boot.PostCode0",
3629 "/xyz/openbmc_project/State/Boot/PostCode0",
3630 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3631 });
George Liu647b3cd2021-07-05 12:43:56 +08003632}
3633
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003634inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003635{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003636 BMCWEB_ROUTE(
3637 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003638 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003639 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003640 [&app](const crow::Request& req,
3641 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3642 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003643 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003644 {
3645 return;
3646 }
3647 uint16_t bootIndex = 0;
3648 uint64_t codeIndex = 0;
3649 if (!parsePostCode(targetID, codeIndex, bootIndex))
3650 {
3651 // Requested ID was not found
3652 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3653 return;
3654 }
3655 if (bootIndex == 0 || codeIndex == 0)
3656 {
3657 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3658 << targetID;
3659 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003660
Ed Tanous002d39b2022-05-31 08:59:27 -07003661 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3662 asyncResp->res.jsonValue["@odata.id"] =
3663 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3664 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3665 asyncResp->res.jsonValue["Description"] =
3666 "Collection of POST Code Log Entries";
3667 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3668 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003669
Ed Tanous002d39b2022-05-31 08:59:27 -07003670 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3671 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003672}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003673
Ed Tanous1da66f72018-07-27 16:13:37 -07003674} // namespace redfish