blob: 8c062aaaeb64c6de2c8a4c806824066bc625139b [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
575 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
576 entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500577 return;
578 }
579
Ed Tanous002d39b2022-05-31 08:59:27 -0700580 asyncResp->res.jsonValue["@odata.type"] =
581 "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800582 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700583 asyncResp->res.jsonValue["Id"] = entryID;
584 asyncResp->res.jsonValue["EntryType"] = "Event";
585 asyncResp->res.jsonValue["Created"] =
Ed Tanous2b829372022-08-03 14:22:34 -0700586 redfish::time_utils::getDateTimeUint(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700587 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500588
Ed Tanous002d39b2022-05-31 08:59:27 -0700589 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500590 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700591 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
592 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800593 entriesPath + entryID + "/attachment";
594 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500595 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700596 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500597 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700598 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
599 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
600 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800601 entriesPath + entryID + "/attachment";
602 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500603 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700604 }
605 if (!foundDumpEntry)
606 {
607 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
608 messages::internalError(asyncResp->res);
609 return;
610 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500611 },
612 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
613 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
614}
615
zhanghch058d1b46d2021-04-01 11:18:24 +0800616inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800617 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500618 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500619{
Ed Tanous002d39b2022-05-31 08:59:27 -0700620 auto respHandler =
621 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500622 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
623 if (ec)
624 {
George Liu3de8d8b2021-03-22 17:49:39 +0800625 if (ec.value() == EBADR)
626 {
627 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
628 return;
629 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500630 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -0800631 << ec << " entryID=" << entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500632 messages::internalError(asyncResp->res);
633 return;
634 }
635 };
636 crow::connections::systemBus->async_method_call(
637 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500638 "/xyz/openbmc_project/dump/" +
639 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
640 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500641 "xyz.openbmc_project.Object.Delete", "Delete");
642}
643
zhanghch058d1b46d2021-04-01 11:18:24 +0800644inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700645 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800646 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
647 const uint32_t& dumpId, const std::string& dumpPath,
648 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500649{
650 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -0500651 [dumpId, dumpPath,
652 dumpType](boost::system::error_code err, sdbusplus::message_t& m,
653 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700654 if (err)
655 {
656 BMCWEB_LOG_ERROR << "Error in creating a dump";
657 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500658 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700659 }
660
661 dbus::utility::DBusInteracesMap interfacesList;
662
663 sdbusplus::message::object_path objPath;
664
665 m.read(objPath, interfacesList);
666
667 if (objPath.str ==
668 "/xyz/openbmc_project/dump/" +
669 std::string(boost::algorithm::to_lower_copy(dumpType)) +
670 "/entry/" + std::to_string(dumpId))
671 {
672 nlohmann::json retMessage = messages::success();
673 taskData->messages.emplace_back(retMessage);
674
675 std::string headerLoc =
676 "Location: " + dumpPath + std::to_string(dumpId);
677 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
678
679 taskData->state = "Completed";
680 return task::completed;
681 }
682 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500683 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800684 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500685 "member='InterfacesAdded', "
686 "path='/xyz/openbmc_project/dump'");
687
688 task->startTimer(std::chrono::minutes(3));
689 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700690 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500691}
692
zhanghch058d1b46d2021-04-01 11:18:24 +0800693inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
694 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500695{
Claire Weinanfdd26902022-03-01 14:18:25 -0800696 std::string dumpPath = getDumpEntriesPath(dumpType);
697 if (dumpPath.empty())
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500698 {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500699 messages::internalError(asyncResp->res);
700 return;
701 }
702
703 std::optional<std::string> diagnosticDataType;
704 std::optional<std::string> oemDiagnosticDataType;
705
Willy Tu15ed6782021-12-14 11:03:16 -0800706 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500707 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
708 "OEMDiagnosticDataType", oemDiagnosticDataType))
709 {
710 return;
711 }
712
713 if (dumpType == "System")
714 {
715 if (!oemDiagnosticDataType || !diagnosticDataType)
716 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800717 BMCWEB_LOG_ERROR
718 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500719 messages::actionParameterMissing(
720 asyncResp->res, "CollectDiagnosticData",
721 "DiagnosticDataType & OEMDiagnosticDataType");
722 return;
723 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700724 if ((*oemDiagnosticDataType != "System") ||
725 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500726 {
727 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700728 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500729 return;
730 }
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500731 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500732 }
733 else if (dumpType == "BMC")
734 {
735 if (!diagnosticDataType)
736 {
George Liu0fda0f12021-11-16 10:06:17 +0800737 BMCWEB_LOG_ERROR
738 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500739 messages::actionParameterMissing(
740 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
741 return;
742 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700743 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500744 {
745 BMCWEB_LOG_ERROR
746 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700747 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500748 return;
749 }
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500750 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/";
751 }
752 else
753 {
754 BMCWEB_LOG_ERROR << "CreateDump failed. Unknown dump type";
755 messages::internalError(asyncResp->res);
756 return;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500757 }
758
759 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700760 [asyncResp, payload(task::Payload(req)), dumpPath,
761 dumpType](const boost::system::error_code ec,
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500762 const sdbusplus::message::message& msg,
Ed Tanous98be3e32021-09-16 15:05:36 -0700763 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700764 if (ec)
765 {
766 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500767 const sd_bus_error* dbusError = msg.get_error();
768 if (dbusError == nullptr)
769 {
770 messages::internalError(asyncResp->res);
771 return;
772 }
773
774 BMCWEB_LOG_ERROR << "CreateDump DBus error: " << dbusError->name
775 << " and error msg: " << dbusError->message;
776 if (std::string_view(
777 "xyz.openbmc_project.Common.Error.NotAllowed") ==
778 dbusError->name)
779 {
780 messages::resourceInStandby(asyncResp->res);
781 return;
782 }
783 if (std::string_view(
784 "xyz.openbmc_project.Dump.Create.Error.Disabled") ==
785 dbusError->name)
786 {
787 messages::serviceDisabled(asyncResp->res, dumpPath);
788 return;
789 }
790 if (std::string_view(
791 "xyz.openbmc_project.Common.Error.Unavailable") ==
792 dbusError->name)
793 {
794 messages::resourceInUse(asyncResp->res);
795 return;
796 }
797 // Other Dbus errors such as:
798 // xyz.openbmc_project.Common.Error.InvalidArgument &
799 // org.freedesktop.DBus.Error.InvalidArgs are all related to
800 // the dbus call that is made here in the bmcweb
801 // implementation and has nothing to do with the client's
802 // input in the request. Hence, returning internal error
803 // back to the client.
Ed Tanous002d39b2022-05-31 08:59:27 -0700804 messages::internalError(asyncResp->res);
805 return;
806 }
807 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500808
Ed Tanous002d39b2022-05-31 08:59:27 -0700809 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
810 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500811 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500812 "xyz.openbmc_project.Dump.Manager",
813 "/xyz/openbmc_project/dump/" +
814 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500815 "xyz.openbmc_project.Dump.Create", "CreateDump");
816}
817
zhanghch058d1b46d2021-04-01 11:18:24 +0800818inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
819 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500820{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500821 std::string dumpTypeLowerCopy =
822 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800823
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500824 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800825 [asyncResp, dumpType](
826 const boost::system::error_code ec,
827 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700828 if (ec)
829 {
830 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
831 messages::internalError(asyncResp->res);
832 return;
833 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500834
Ed Tanous002d39b2022-05-31 08:59:27 -0700835 for (const std::string& path : subTreePaths)
836 {
837 sdbusplus::message::object_path objPath(path);
838 std::string logID = objPath.filename();
839 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500840 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700841 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500842 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700843 deleteDumpEntry(asyncResp, logID, dumpType);
844 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500845 },
846 "xyz.openbmc_project.ObjectMapper",
847 "/xyz/openbmc_project/object_mapper",
848 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500849 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
850 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
851 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500852}
853
Ed Tanousb9d36b42022-02-26 21:42:46 -0800854inline static void
855 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
856 std::string& filename, std::string& timestamp,
857 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700858{
859 for (auto property : params)
860 {
861 if (property.first == "Timestamp")
862 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500863 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500864 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700865 if (value != nullptr)
866 {
867 timestamp = *value;
868 }
869 }
870 else if (property.first == "Filename")
871 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500872 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500873 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700874 if (value != nullptr)
875 {
876 filename = *value;
877 }
878 }
879 else if (property.first == "Log")
880 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500881 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500882 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700883 if (value != nullptr)
884 {
885 logfile = *value;
886 }
887 }
888 }
889}
890
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500891constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700892inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700893{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800894 /**
895 * Functions triggers appropriate requests on DBus
896 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700897 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700898 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700899 .methods(boost::beast::http::verb::get)(
900 [&app](const crow::Request& req,
901 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000902 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700903 {
904 return;
905 }
906 // Collections don't include the static data added by SubRoute
907 // because it has a duplicate entry for members
908 asyncResp->res.jsonValue["@odata.type"] =
909 "#LogServiceCollection.LogServiceCollection";
910 asyncResp->res.jsonValue["@odata.id"] =
911 "/redfish/v1/Systems/system/LogServices";
912 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
913 asyncResp->res.jsonValue["Description"] =
914 "Collection of LogServices for this Computer System";
915 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
916 logServiceArray = nlohmann::json::array();
917 nlohmann::json::object_t eventLog;
918 eventLog["@odata.id"] =
919 "/redfish/v1/Systems/system/LogServices/EventLog";
920 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500921#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700922 nlohmann::json::object_t dumpLog;
923 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
924 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600925#endif
926
Jason M. Billsd53dd412019-02-12 17:16:22 -0800927#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700928 nlohmann::json::object_t crashdump;
929 crashdump["@odata.id"] =
930 "/redfish/v1/Systems/system/LogServices/Crashdump";
931 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800932#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800933
934#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700935 nlohmann::json::object_t hostlogger;
936 hostlogger["@odata.id"] =
937 "/redfish/v1/Systems/system/LogServices/HostLogger";
938 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800939#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700940 asyncResp->res.jsonValue["Members@odata.count"] =
941 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800942
Ed Tanous002d39b2022-05-31 08:59:27 -0700943 crow::connections::systemBus->async_method_call(
944 [asyncResp](const boost::system::error_code ec,
945 const dbus::utility::MapperGetSubTreePathsResponse&
946 subtreePath) {
947 if (ec)
948 {
949 BMCWEB_LOG_ERROR << ec;
950 return;
951 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700952
Ed Tanous002d39b2022-05-31 08:59:27 -0700953 for (const auto& pathStr : subtreePath)
954 {
955 if (pathStr.find("PostCode") != std::string::npos)
956 {
957 nlohmann::json& logServiceArrayLocal =
958 asyncResp->res.jsonValue["Members"];
959 logServiceArrayLocal.push_back(
960 {{"@odata.id",
961 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
962 asyncResp->res.jsonValue["Members@odata.count"] =
963 logServiceArrayLocal.size();
964 return;
965 }
966 }
967 },
968 "xyz.openbmc_project.ObjectMapper",
969 "/xyz/openbmc_project/object_mapper",
970 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
971 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700972 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700973}
974
975inline void requestRoutesEventLogService(App& app)
976{
977 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700978 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700979 .methods(boost::beast::http::verb::get)(
980 [&app](const crow::Request& req,
981 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000982 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700983 {
984 return;
985 }
986 asyncResp->res.jsonValue["@odata.id"] =
987 "/redfish/v1/Systems/system/LogServices/EventLog";
988 asyncResp->res.jsonValue["@odata.type"] =
989 "#LogService.v1_1_0.LogService";
990 asyncResp->res.jsonValue["Name"] = "Event Log Service";
991 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
992 asyncResp->res.jsonValue["Id"] = "EventLog";
993 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530994
Ed Tanous002d39b2022-05-31 08:59:27 -0700995 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -0700996 redfish::time_utils::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530997
Ed Tanous002d39b2022-05-31 08:59:27 -0700998 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
999 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1000 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05301001
Ed Tanous002d39b2022-05-31 08:59:27 -07001002 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1003 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1004 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001005
Ed Tanous002d39b2022-05-31 08:59:27 -07001006 {"target",
1007 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001008 });
1009}
1010
1011inline void requestRoutesJournalEventLogClear(App& app)
1012{
Jason M. Bills4978b632022-02-22 14:17:43 -08001013 BMCWEB_ROUTE(
1014 app,
1015 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001016 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001017 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001018 [&app](const crow::Request& req,
1019 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001020 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001021 {
1022 return;
1023 }
1024 // Clear the EventLog by deleting the log files
1025 std::vector<std::filesystem::path> redfishLogFiles;
1026 if (getRedfishLogFiles(redfishLogFiles))
1027 {
1028 for (const std::filesystem::path& file : redfishLogFiles)
1029 {
1030 std::error_code ec;
1031 std::filesystem::remove(file, ec);
1032 }
1033 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001034
Ed Tanous002d39b2022-05-31 08:59:27 -07001035 // Reload rsyslog so it knows to start new log files
1036 crow::connections::systemBus->async_method_call(
1037 [asyncResp](const boost::system::error_code ec) {
1038 if (ec)
1039 {
1040 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1041 messages::internalError(asyncResp->res);
1042 return;
1043 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001044
Ed Tanous002d39b2022-05-31 08:59:27 -07001045 messages::success(asyncResp->res);
1046 },
1047 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1048 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1049 "replace");
1050 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001051}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001052
Jason M. Billsac992cd2022-06-24 13:31:46 -07001053enum class LogParseError
1054{
1055 success,
1056 parseFailed,
1057 messageIdNotInRegistry,
1058};
1059
1060static LogParseError
1061 fillEventLogEntryJson(const std::string& logEntryID,
1062 const std::string& logEntry,
1063 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001064{
Jason M. Bills95820182019-04-22 16:25:34 -07001065 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001066 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001067 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001068 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001069 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001070 return LogParseError::parseFailed;
Jason M. Bills95820182019-04-22 16:25:34 -07001071 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001072 std::string timestamp = logEntry.substr(0, space);
1073 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001074 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001075 if (entryStart == std::string::npos)
1076 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001077 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001078 }
1079 std::string_view entry(logEntry);
1080 entry.remove_prefix(entryStart);
1081 // Use split to separate the entry into its fields
1082 std::vector<std::string> logEntryFields;
1083 boost::split(logEntryFields, entry, boost::is_any_of(","),
1084 boost::token_compress_on);
1085 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001086 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001087 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001088 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001089 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001090 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001091
Jason M. Bills4851d452019-03-28 11:27:48 -07001092 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001093 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001094
Sui Chen54417b02022-03-24 14:59:52 -07001095 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001096 {
Sui Chen54417b02022-03-24 14:59:52 -07001097 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001098 return LogParseError::messageIdNotInRegistry;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001099 }
1100
Sui Chen54417b02022-03-24 14:59:52 -07001101 std::string msg = message->message;
1102
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001103 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001104 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001105 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001106 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001107 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001108 // If the first string is empty, assume there are no MessageArgs
1109 std::size_t messageArgsSize = 0;
1110 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001111 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001112 messageArgsSize = logEntryFields.size() - 1;
1113 }
1114
Ed Tanous23a21a12020-07-25 04:45:05 +00001115 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001116
1117 // Fill the MessageArgs into the Message
1118 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001119 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001120 {
1121 std::string argStr = "%" + std::to_string(++i);
1122 size_t argPos = msg.find(argStr);
1123 if (argPos != std::string::npos)
1124 {
1125 msg.replace(argPos, argStr.length(), messageArg);
1126 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001127 }
1128 }
1129
Jason M. Bills95820182019-04-22 16:25:34 -07001130 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1131 // format which matches the Redfish format except for the fractional seconds
1132 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001133 std::size_t dot = timestamp.find_first_of('.');
1134 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001135 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001136 {
Jason M. Bills95820182019-04-22 16:25:34 -07001137 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001138 }
1139
1140 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07001141 logEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1142 logEntryJson["@odata.id"] =
1143 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1144 logEntryJson["Name"] = "System Event Log Entry";
1145 logEntryJson["Id"] = logEntryID;
1146 logEntryJson["Message"] = std::move(msg);
1147 logEntryJson["MessageId"] = std::move(messageID);
1148 logEntryJson["MessageArgs"] = messageArgs;
1149 logEntryJson["EntryType"] = "Event";
1150 logEntryJson["Severity"] = message->messageSeverity;
1151 logEntryJson["Created"] = std::move(timestamp);
Jason M. Billsac992cd2022-06-24 13:31:46 -07001152 return LogParseError::success;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001153}
1154
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001155inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001156{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001157 BMCWEB_ROUTE(app,
1158 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001159 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001160 .methods(boost::beast::http::verb::get)(
1161 [&app](const crow::Request& req,
1162 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1163 query_param::QueryCapabilities capabilities = {
1164 .canDelegateTop = true,
1165 .canDelegateSkip = true,
1166 };
1167 query_param::Query delegatedQuery;
1168 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001169 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001170 {
1171 return;
1172 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001173 size_t top =
1174 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
1175 size_t skip = delegatedQuery.skip.value_or(0);
1176
Ed Tanous002d39b2022-05-31 08:59:27 -07001177 // Collections don't include the static data added by SubRoute
1178 // because it has a duplicate entry for members
1179 asyncResp->res.jsonValue["@odata.type"] =
1180 "#LogEntryCollection.LogEntryCollection";
1181 asyncResp->res.jsonValue["@odata.id"] =
1182 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1183 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1184 asyncResp->res.jsonValue["Description"] =
1185 "Collection of System Event Log Entries";
1186
1187 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1188 logEntryArray = nlohmann::json::array();
1189 // Go through the log files and create a unique ID for each
1190 // entry
1191 std::vector<std::filesystem::path> redfishLogFiles;
1192 getRedfishLogFiles(redfishLogFiles);
1193 uint64_t entryCount = 0;
1194 std::string logEntry;
1195
1196 // Oldest logs are in the last file, so start there and loop
1197 // backwards
1198 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1199 it++)
1200 {
1201 std::ifstream logStream(*it);
1202 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001203 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001204 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001205 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001206
Ed Tanous002d39b2022-05-31 08:59:27 -07001207 // Reset the unique ID on the first entry
1208 bool firstEntry = true;
1209 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001210 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001211 std::string idStr;
1212 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001213 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001214 continue;
1215 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001216 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001217
Jason M. Billsde703c52022-06-23 14:19:04 -07001218 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001219 LogParseError status =
1220 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1221 if (status == LogParseError::messageIdNotInRegistry)
1222 {
1223 continue;
1224 }
1225 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001226 {
1227 messages::internalError(asyncResp->res);
1228 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001229 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001230
Jason M. Billsde703c52022-06-23 14:19:04 -07001231 entryCount++;
1232 // Handle paging using skip (number of entries to skip from the
1233 // start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07001234 if (entryCount <= skip || entryCount > skip + top)
Jason M. Billsde703c52022-06-23 14:19:04 -07001235 {
1236 continue;
1237 }
1238
1239 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001240 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001241 }
1242 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001243 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07001244 {
1245 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1246 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001247 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07001248 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001249 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001250}
Chicago Duan336e96c2019-07-15 14:22:08 +08001251
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001252inline void requestRoutesJournalEventLogEntry(App& app)
1253{
1254 BMCWEB_ROUTE(
1255 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001256 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001257 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001258 [&app](const crow::Request& req,
1259 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1260 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001261 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001262 {
1263 return;
1264 }
1265 const std::string& targetID = param;
1266
1267 // Go through the log files and check the unique ID for each
1268 // entry to find the target entry
1269 std::vector<std::filesystem::path> redfishLogFiles;
1270 getRedfishLogFiles(redfishLogFiles);
1271 std::string logEntry;
1272
1273 // Oldest logs are in the last file, so start there and loop
1274 // backwards
1275 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1276 it++)
1277 {
1278 std::ifstream logStream(*it);
1279 if (!logStream.is_open())
1280 {
1281 continue;
1282 }
1283
1284 // Reset the unique ID on the first entry
1285 bool firstEntry = true;
1286 while (std::getline(logStream, logEntry))
1287 {
1288 std::string idStr;
1289 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001290 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001291 continue;
1292 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001293 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001294
1295 if (idStr == targetID)
1296 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001297 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001298 LogParseError status =
1299 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1300 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001301 {
1302 messages::internalError(asyncResp->res);
1303 return;
1304 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07001305 asyncResp->res.jsonValue.update(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001306 return;
1307 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001308 }
1309 }
1310 // Requested ID was not found
1311 messages::resourceMissingAtURI(asyncResp->res,
1312 crow::utility::urlFromPieces(targetID));
1313 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001314}
1315
1316inline void requestRoutesDBusEventLogEntryCollection(App& app)
1317{
1318 BMCWEB_ROUTE(app,
1319 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001320 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001321 .methods(boost::beast::http::verb::get)(
1322 [&app](const crow::Request& req,
1323 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001324 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001325 {
1326 return;
1327 }
1328 // Collections don't include the static data added by SubRoute
1329 // because it has a duplicate entry for members
1330 asyncResp->res.jsonValue["@odata.type"] =
1331 "#LogEntryCollection.LogEntryCollection";
1332 asyncResp->res.jsonValue["@odata.id"] =
1333 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1334 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1335 asyncResp->res.jsonValue["Description"] =
1336 "Collection of System Event Log Entries";
1337
1338 // DBus implementation of EventLog/Entries
1339 // Make call to Logging Service to find all log entry objects
1340 crow::connections::systemBus->async_method_call(
1341 [asyncResp](const boost::system::error_code ec,
1342 const dbus::utility::ManagedObjectType& resp) {
1343 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001344 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001345 // TODO Handle for specific error code
1346 BMCWEB_LOG_ERROR
1347 << "getLogEntriesIfaceData resp_handler got error " << ec;
1348 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001349 return;
1350 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001351 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1352 entriesArray = nlohmann::json::array();
1353 for (const auto& objectPath : resp)
1354 {
1355 const uint32_t* id = nullptr;
1356 const uint64_t* timestamp = nullptr;
1357 const uint64_t* updateTimestamp = nullptr;
1358 const std::string* severity = nullptr;
1359 const std::string* message = nullptr;
1360 const std::string* filePath = nullptr;
1361 bool resolved = false;
1362 for (const auto& interfaceMap : objectPath.second)
1363 {
1364 if (interfaceMap.first ==
1365 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001366 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001367 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001368 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001369 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001370 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001371 id = std::get_if<uint32_t>(&propertyMap.second);
1372 }
1373 else if (propertyMap.first == "Timestamp")
1374 {
1375 timestamp =
1376 std::get_if<uint64_t>(&propertyMap.second);
1377 }
1378 else if (propertyMap.first == "UpdateTimestamp")
1379 {
1380 updateTimestamp =
1381 std::get_if<uint64_t>(&propertyMap.second);
1382 }
1383 else if (propertyMap.first == "Severity")
1384 {
1385 severity = std::get_if<std::string>(
1386 &propertyMap.second);
1387 }
1388 else if (propertyMap.first == "Message")
1389 {
1390 message = std::get_if<std::string>(
1391 &propertyMap.second);
1392 }
1393 else if (propertyMap.first == "Resolved")
1394 {
1395 const bool* resolveptr =
1396 std::get_if<bool>(&propertyMap.second);
1397 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001398 {
1399 messages::internalError(asyncResp->res);
1400 return;
1401 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001402 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001403 }
1404 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001405 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001406 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001407 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001408 messages::internalError(asyncResp->res);
1409 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001410 }
1411 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001412 else if (interfaceMap.first ==
1413 "xyz.openbmc_project.Common.FilePath")
1414 {
1415 for (const auto& propertyMap : interfaceMap.second)
1416 {
1417 if (propertyMap.first == "Path")
1418 {
1419 filePath = std::get_if<std::string>(
1420 &propertyMap.second);
1421 }
1422 }
1423 }
1424 }
1425 // Object path without the
1426 // xyz.openbmc_project.Logging.Entry interface, ignore
1427 // and continue.
1428 if (id == nullptr || message == nullptr ||
1429 severity == nullptr || timestamp == nullptr ||
1430 updateTimestamp == nullptr)
1431 {
1432 continue;
1433 }
1434 entriesArray.push_back({});
1435 nlohmann::json& thisEntry = entriesArray.back();
1436 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1437 thisEntry["@odata.id"] =
1438 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1439 std::to_string(*id);
1440 thisEntry["Name"] = "System Event Log Entry";
1441 thisEntry["Id"] = std::to_string(*id);
1442 thisEntry["Message"] = *message;
1443 thisEntry["Resolved"] = resolved;
1444 thisEntry["EntryType"] = "Event";
1445 thisEntry["Severity"] =
1446 translateSeverityDbusToRedfish(*severity);
1447 thisEntry["Created"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001448 redfish::time_utils::getDateTimeUintMs(*timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001449 thisEntry["Modified"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001450 redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001451 if (filePath != nullptr)
1452 {
1453 thisEntry["AdditionalDataURI"] =
1454 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1455 std::to_string(*id) + "/attachment";
1456 }
1457 }
1458 std::sort(
1459 entriesArray.begin(), entriesArray.end(),
1460 [](const nlohmann::json& left, const nlohmann::json& right) {
1461 return (left["Id"] <= right["Id"]);
1462 });
1463 asyncResp->res.jsonValue["Members@odata.count"] =
1464 entriesArray.size();
1465 },
1466 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1467 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001468 });
1469}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001470
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001471inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001472{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001473 BMCWEB_ROUTE(
1474 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001475 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001476 .methods(boost::beast::http::verb::get)(
1477 [&app](const crow::Request& req,
1478 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1479 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001480 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001481 {
1482 return;
1483 }
1484 std::string entryID = param;
1485 dbus::utility::escapePathForDbus(entryID);
1486
1487 // DBus implementation of EventLog/Entries
1488 // Make call to Logging Service to find all log entry objects
1489 crow::connections::systemBus->async_method_call(
1490 [asyncResp, entryID](const boost::system::error_code ec,
1491 const dbus::utility::DBusPropertiesMap& resp) {
1492 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001493 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001494 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1495 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001496 return;
1497 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001498 if (ec)
1499 {
1500 BMCWEB_LOG_ERROR
1501 << "EventLogEntry (DBus) resp_handler got error " << ec;
1502 messages::internalError(asyncResp->res);
1503 return;
1504 }
1505 const uint32_t* id = nullptr;
1506 const uint64_t* timestamp = nullptr;
1507 const uint64_t* updateTimestamp = nullptr;
1508 const std::string* severity = nullptr;
1509 const std::string* message = nullptr;
1510 const std::string* filePath = nullptr;
1511 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001512
Ed Tanous002d39b2022-05-31 08:59:27 -07001513 for (const auto& propertyMap : resp)
1514 {
1515 if (propertyMap.first == "Id")
1516 {
1517 id = std::get_if<uint32_t>(&propertyMap.second);
1518 }
1519 else if (propertyMap.first == "Timestamp")
1520 {
1521 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1522 }
1523 else if (propertyMap.first == "UpdateTimestamp")
1524 {
1525 updateTimestamp =
1526 std::get_if<uint64_t>(&propertyMap.second);
1527 }
1528 else if (propertyMap.first == "Severity")
1529 {
1530 severity = std::get_if<std::string>(&propertyMap.second);
1531 }
1532 else if (propertyMap.first == "Message")
1533 {
1534 message = std::get_if<std::string>(&propertyMap.second);
1535 }
1536 else if (propertyMap.first == "Resolved")
1537 {
1538 const bool* resolveptr =
1539 std::get_if<bool>(&propertyMap.second);
1540 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001541 {
1542 messages::internalError(asyncResp->res);
1543 return;
1544 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001545 resolved = *resolveptr;
1546 }
1547 else if (propertyMap.first == "Path")
1548 {
1549 filePath = std::get_if<std::string>(&propertyMap.second);
1550 }
1551 }
1552 if (id == nullptr || message == nullptr || severity == nullptr ||
1553 timestamp == nullptr || updateTimestamp == nullptr)
1554 {
1555 messages::internalError(asyncResp->res);
1556 return;
1557 }
1558 asyncResp->res.jsonValue["@odata.type"] =
1559 "#LogEntry.v1_8_0.LogEntry";
1560 asyncResp->res.jsonValue["@odata.id"] =
1561 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1562 std::to_string(*id);
1563 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1564 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1565 asyncResp->res.jsonValue["Message"] = *message;
1566 asyncResp->res.jsonValue["Resolved"] = resolved;
1567 asyncResp->res.jsonValue["EntryType"] = "Event";
1568 asyncResp->res.jsonValue["Severity"] =
1569 translateSeverityDbusToRedfish(*severity);
1570 asyncResp->res.jsonValue["Created"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001571 redfish::time_utils::getDateTimeUintMs(*timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001572 asyncResp->res.jsonValue["Modified"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001573 redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001574 if (filePath != nullptr)
1575 {
1576 asyncResp->res.jsonValue["AdditionalDataURI"] =
1577 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1578 std::to_string(*id) + "/attachment";
1579 }
1580 },
1581 "xyz.openbmc_project.Logging",
1582 "/xyz/openbmc_project/logging/entry/" + entryID,
1583 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001584 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001585
1586 BMCWEB_ROUTE(
1587 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001588 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001589 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001590 [&app](const crow::Request& req,
1591 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1592 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001593 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001594 {
1595 return;
1596 }
1597 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001598
Ed Tanous002d39b2022-05-31 08:59:27 -07001599 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1600 resolved))
1601 {
1602 return;
1603 }
1604 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001605
Ed Tanous002d39b2022-05-31 08:59:27 -07001606 crow::connections::systemBus->async_method_call(
1607 [asyncResp, entryId](const boost::system::error_code ec) {
1608 if (ec)
1609 {
1610 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1611 messages::internalError(asyncResp->res);
1612 return;
1613 }
1614 },
1615 "xyz.openbmc_project.Logging",
1616 "/xyz/openbmc_project/logging/entry/" + entryId,
1617 "org.freedesktop.DBus.Properties", "Set",
1618 "xyz.openbmc_project.Logging.Entry", "Resolved",
1619 dbus::utility::DbusVariantType(*resolved));
1620 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001621
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001622 BMCWEB_ROUTE(
1623 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001624 .privileges(redfish::privileges::deleteLogEntry)
1625
Ed Tanous002d39b2022-05-31 08:59:27 -07001626 .methods(boost::beast::http::verb::delete_)(
1627 [&app](const crow::Request& req,
1628 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1629 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001630 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001631 {
1632 return;
1633 }
1634 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1635
1636 std::string entryID = param;
1637
1638 dbus::utility::escapePathForDbus(entryID);
1639
1640 // Process response from Logging service.
1641 auto respHandler =
1642 [asyncResp, entryID](const boost::system::error_code ec) {
1643 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1644 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001645 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001646 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001647 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001648 messages::resourceNotFound(asyncResp->res, "LogEntry",
1649 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001650 return;
1651 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001652 // TODO Handle for specific error code
1653 BMCWEB_LOG_ERROR
1654 << "EventLogEntry (DBus) doDelete respHandler got error "
1655 << ec;
1656 asyncResp->res.result(
1657 boost::beast::http::status::internal_server_error);
1658 return;
1659 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660
Ed Tanous002d39b2022-05-31 08:59:27 -07001661 asyncResp->res.result(boost::beast::http::status::ok);
1662 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001663
Ed Tanous002d39b2022-05-31 08:59:27 -07001664 // Make call to Logging service to request Delete Log
1665 crow::connections::systemBus->async_method_call(
1666 respHandler, "xyz.openbmc_project.Logging",
1667 "/xyz/openbmc_project/logging/entry/" + entryID,
1668 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001669 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001670}
1671
1672inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001673{
George Liu0fda0f12021-11-16 10:06:17 +08001674 BMCWEB_ROUTE(
1675 app,
1676 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001677 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001678 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001679 [&app](const crow::Request& req,
1680 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1681 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001682 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001683 {
1684 return;
1685 }
1686 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1687 {
1688 asyncResp->res.result(boost::beast::http::status::bad_request);
1689 return;
1690 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001691
Ed Tanous002d39b2022-05-31 08:59:27 -07001692 std::string entryID = param;
1693 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001694
Ed Tanous002d39b2022-05-31 08:59:27 -07001695 crow::connections::systemBus->async_method_call(
1696 [asyncResp, entryID](const boost::system::error_code ec,
1697 const sdbusplus::message::unix_fd& unixfd) {
1698 if (ec.value() == EBADR)
1699 {
1700 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1701 entryID);
1702 return;
1703 }
1704 if (ec)
1705 {
1706 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1707 messages::internalError(asyncResp->res);
1708 return;
1709 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001710
Ed Tanous002d39b2022-05-31 08:59:27 -07001711 int fd = -1;
1712 fd = dup(unixfd);
1713 if (fd == -1)
1714 {
1715 messages::internalError(asyncResp->res);
1716 return;
1717 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001718
Ed Tanous002d39b2022-05-31 08:59:27 -07001719 long long int size = lseek(fd, 0, SEEK_END);
1720 if (size == -1)
1721 {
1722 messages::internalError(asyncResp->res);
1723 return;
1724 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001725
Ed Tanous002d39b2022-05-31 08:59:27 -07001726 // Arbitrary max size of 64kb
1727 constexpr int maxFileSize = 65536;
1728 if (size > maxFileSize)
1729 {
1730 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1731 << maxFileSize;
1732 messages::internalError(asyncResp->res);
1733 return;
1734 }
1735 std::vector<char> data(static_cast<size_t>(size));
1736 long long int rc = lseek(fd, 0, SEEK_SET);
1737 if (rc == -1)
1738 {
1739 messages::internalError(asyncResp->res);
1740 return;
1741 }
1742 rc = read(fd, data.data(), data.size());
1743 if ((rc == -1) || (rc != size))
1744 {
1745 messages::internalError(asyncResp->res);
1746 return;
1747 }
1748 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001749
Ed Tanous002d39b2022-05-31 08:59:27 -07001750 std::string_view strData(data.data(), data.size());
1751 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001752
Ed Tanousd9f6c622022-03-17 09:12:17 -07001753 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07001754 "application/octet-stream");
Ed Tanousd9f6c622022-03-17 09:12:17 -07001755 asyncResp->res.addHeader(
1756 boost::beast::http::field::content_transfer_encoding, "Base64");
Ed Tanous002d39b2022-05-31 08:59:27 -07001757 asyncResp->res.body() = std::move(output);
1758 },
1759 "xyz.openbmc_project.Logging",
1760 "/xyz/openbmc_project/logging/entry/" + entryID,
1761 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1762 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001763}
1764
Spencer Kub7028eb2021-10-26 15:27:35 +08001765constexpr const char* hostLoggerFolderPath = "/var/log/console";
1766
1767inline bool
1768 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1769 std::vector<std::filesystem::path>& hostLoggerFiles)
1770{
1771 std::error_code ec;
1772 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1773 if (ec)
1774 {
1775 BMCWEB_LOG_ERROR << ec.message();
1776 return false;
1777 }
1778 for (const std::filesystem::directory_entry& it : logPath)
1779 {
1780 std::string filename = it.path().filename();
1781 // Prefix of each log files is "log". Find the file and save the
1782 // path
Ed Tanous11ba3972022-07-11 09:50:41 -07001783 if (filename.starts_with("log"))
Spencer Kub7028eb2021-10-26 15:27:35 +08001784 {
1785 hostLoggerFiles.emplace_back(it.path());
1786 }
1787 }
1788 // As the log files rotate, they are appended with a ".#" that is higher for
1789 // the older logs. Since we start from oldest logs, sort the name in
1790 // descending order.
1791 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1792 AlphanumLess<std::string>());
1793
1794 return true;
1795}
1796
Ed Tanous02cad962022-06-30 16:50:15 -07001797inline bool getHostLoggerEntries(
1798 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1799 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001800{
1801 GzFileReader logFile;
1802
1803 // Go though all log files and expose host logs.
1804 for (const std::filesystem::path& it : hostLoggerFiles)
1805 {
1806 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1807 {
1808 BMCWEB_LOG_ERROR << "fail to expose host logs";
1809 return false;
1810 }
1811 }
1812 // Get lastMessage from constructor by getter
1813 std::string lastMessage = logFile.getLastMessage();
1814 if (!lastMessage.empty())
1815 {
1816 logCount++;
1817 if (logCount > skip && logCount <= (skip + top))
1818 {
1819 logEntries.push_back(lastMessage);
1820 }
1821 }
1822 return true;
1823}
1824
1825inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1826 const std::string& msg,
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001827 nlohmann::json::object_t& logEntryJson)
Spencer Kub7028eb2021-10-26 15:27:35 +08001828{
1829 // Fill in the log entry with the gathered data.
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001830 logEntryJson["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
1831 logEntryJson["@odata.id"] =
1832 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1833 logEntryID;
1834 logEntryJson["Name"] = "Host Logger Entry";
1835 logEntryJson["Id"] = logEntryID;
1836 logEntryJson["Message"] = msg;
1837 logEntryJson["EntryType"] = "Oem";
1838 logEntryJson["Severity"] = "OK";
1839 logEntryJson["OemRecordFormat"] = "Host Logger Entry";
Spencer Kub7028eb2021-10-26 15:27:35 +08001840}
1841
1842inline void requestRoutesSystemHostLogger(App& app)
1843{
1844 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1845 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001846 .methods(boost::beast::http::verb::get)(
1847 [&app](const crow::Request& req,
1848 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001849 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001850 {
1851 return;
1852 }
1853 asyncResp->res.jsonValue["@odata.id"] =
1854 "/redfish/v1/Systems/system/LogServices/HostLogger";
1855 asyncResp->res.jsonValue["@odata.type"] =
1856 "#LogService.v1_1_0.LogService";
1857 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1858 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1859 asyncResp->res.jsonValue["Id"] = "HostLogger";
1860 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1861 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1862 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001863}
1864
1865inline void requestRoutesSystemHostLoggerCollection(App& app)
1866{
1867 BMCWEB_ROUTE(app,
1868 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1869 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001870 .methods(boost::beast::http::verb::get)(
1871 [&app](const crow::Request& req,
1872 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1873 query_param::QueryCapabilities capabilities = {
1874 .canDelegateTop = true,
1875 .canDelegateSkip = true,
1876 };
1877 query_param::Query delegatedQuery;
1878 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001879 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001880 {
1881 return;
1882 }
1883 asyncResp->res.jsonValue["@odata.id"] =
1884 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1885 asyncResp->res.jsonValue["@odata.type"] =
1886 "#LogEntryCollection.LogEntryCollection";
1887 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1888 asyncResp->res.jsonValue["Description"] =
1889 "Collection of HostLogger Entries";
1890 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1891 logEntryArray = nlohmann::json::array();
1892 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001893
Ed Tanous002d39b2022-05-31 08:59:27 -07001894 std::vector<std::filesystem::path> hostLoggerFiles;
1895 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1896 {
1897 BMCWEB_LOG_ERROR << "fail to get host log file path";
1898 return;
1899 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001900 // If we weren't provided top and skip limits, use the defaults.
1901 size_t skip = delegatedQuery.skip.value_or(0);
1902 size_t top =
1903 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
Ed Tanous002d39b2022-05-31 08:59:27 -07001904 size_t logCount = 0;
1905 // This vector only store the entries we want to expose that
1906 // control by skip and top.
1907 std::vector<std::string> logEntries;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001908 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1909 logCount))
Ed Tanous002d39b2022-05-31 08:59:27 -07001910 {
1911 messages::internalError(asyncResp->res);
1912 return;
1913 }
1914 // If vector is empty, that means skip value larger than total
1915 // log count
1916 if (logEntries.empty())
1917 {
1918 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1919 return;
1920 }
1921 if (!logEntries.empty())
1922 {
1923 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001924 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001925 nlohmann::json::object_t hostLogEntry;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001926 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
1927 hostLogEntry);
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001928 logEntryArray.push_back(std::move(hostLogEntry));
George Liu0fda0f12021-11-16 10:06:17 +08001929 }
1930
Ed Tanous002d39b2022-05-31 08:59:27 -07001931 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001932 if (skip + top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001933 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001934 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1935 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001936 std::to_string(skip + top);
George Liu0fda0f12021-11-16 10:06:17 +08001937 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001938 }
George Liu0fda0f12021-11-16 10:06:17 +08001939 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001940}
1941
1942inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1943{
1944 BMCWEB_ROUTE(
1945 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1946 .privileges(redfish::privileges::getLogEntry)
1947 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001948 [&app](const crow::Request& req,
1949 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1950 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001951 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001952 {
1953 return;
1954 }
1955 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001956
Ed Tanous002d39b2022-05-31 08:59:27 -07001957 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001958
Ed Tanous002d39b2022-05-31 08:59:27 -07001959 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1960 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001961
Ed Tanous002d39b2022-05-31 08:59:27 -07001962 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1963 if (ec == std::errc::invalid_argument)
1964 {
1965 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1966 return;
1967 }
1968 if (ec == std::errc::result_out_of_range)
1969 {
1970 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1971 return;
1972 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001973
Ed Tanous002d39b2022-05-31 08:59:27 -07001974 std::vector<std::filesystem::path> hostLoggerFiles;
1975 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1976 {
1977 BMCWEB_LOG_ERROR << "fail to get host log file path";
1978 return;
1979 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001980
Ed Tanous002d39b2022-05-31 08:59:27 -07001981 size_t logCount = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001982 size_t top = 1;
Ed Tanous002d39b2022-05-31 08:59:27 -07001983 std::vector<std::string> logEntries;
1984 // We can get specific entry by skip and top. For example, if we
1985 // want to get nth entry, we can set skip = n-1 and top = 1 to
1986 // get that entry
1987 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1988 logCount))
1989 {
1990 messages::internalError(asyncResp->res);
1991 return;
1992 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001993
Ed Tanous002d39b2022-05-31 08:59:27 -07001994 if (!logEntries.empty())
1995 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001996 nlohmann::json::object_t hostLogEntry;
1997 fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
1998 asyncResp->res.jsonValue.update(hostLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07001999 return;
2000 }
Spencer Kub7028eb2021-10-26 15:27:35 +08002001
Ed Tanous002d39b2022-05-31 08:59:27 -07002002 // Requested ID was not found
2003 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2004 });
Spencer Kub7028eb2021-10-26 15:27:35 +08002005}
2006
Claire Weinanfdd26902022-03-01 14:18:25 -08002007constexpr char const* dumpManagerIface =
2008 "xyz.openbmc_project.Collection.DeleteAll";
2009inline void handleLogServicesCollectionGet(
2010 crow::App& app, const crow::Request& req,
2011 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2012{
2013 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2014 {
2015 return;
2016 }
2017 // Collections don't include the static data added by SubRoute
2018 // because it has a duplicate entry for members
2019 asyncResp->res.jsonValue["@odata.type"] =
2020 "#LogServiceCollection.LogServiceCollection";
2021 asyncResp->res.jsonValue["@odata.id"] =
2022 "/redfish/v1/Managers/bmc/LogServices";
2023 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2024 asyncResp->res.jsonValue["Description"] =
2025 "Collection of LogServices for this Manager";
2026 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2027 logServiceArray = nlohmann::json::array();
2028
2029#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
2030 logServiceArray.push_back(
2031 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
2032#endif
2033
2034 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2035
2036#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2037 auto respHandler =
2038 [asyncResp](
2039 const boost::system::error_code ec,
2040 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2041 if (ec)
2042 {
2043 BMCWEB_LOG_ERROR
2044 << "handleLogServicesCollectionGet respHandler got error "
2045 << ec;
2046 // Assume that getting an error simply means there are no dump
2047 // LogServices. Return without adding any error response.
2048 return;
2049 }
2050
2051 nlohmann::json& logServiceArrayLocal =
2052 asyncResp->res.jsonValue["Members"];
2053
2054 for (const std::string& path : subTreePaths)
2055 {
2056 if (path == "/xyz/openbmc_project/dump/bmc")
2057 {
2058 logServiceArrayLocal.push_back(
2059 {{"@odata.id",
2060 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
2061 }
2062 else if (path == "/xyz/openbmc_project/dump/faultlog")
2063 {
2064 logServiceArrayLocal.push_back(
2065 {{"@odata.id",
2066 "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
2067 }
2068 }
2069
2070 asyncResp->res.jsonValue["Members@odata.count"] =
2071 logServiceArrayLocal.size();
2072 };
2073
2074 crow::connections::systemBus->async_method_call(
2075 respHandler, "xyz.openbmc_project.ObjectMapper",
2076 "/xyz/openbmc_project/object_mapper",
2077 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2078 "/xyz/openbmc_project/dump", 0,
2079 std::array<const char*, 1>{dumpManagerIface});
2080#endif
2081}
2082
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002083inline void requestRoutesBMCLogServiceCollection(App& app)
2084{
2085 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002086 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002087 .methods(boost::beast::http::verb::get)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002088 std::bind_front(handleLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002089}
Ed Tanous1da66f72018-07-27 16:13:37 -07002090
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002091inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002092{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002093 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002094 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002095 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002096 [&app](const crow::Request& req,
2097 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002098 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002099 {
2100 return;
2101 }
2102 asyncResp->res.jsonValue["@odata.type"] =
2103 "#LogService.v1_1_0.LogService";
2104 asyncResp->res.jsonValue["@odata.id"] =
2105 "/redfish/v1/Managers/bmc/LogServices/Journal";
2106 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2107 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2108 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2109 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302110
Ed Tanous002d39b2022-05-31 08:59:27 -07002111 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002112 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002113 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2114 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2115 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302116
Ed Tanous002d39b2022-05-31 08:59:27 -07002117 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2118 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2119 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002120}
Jason M. Billse1f26342018-07-18 12:12:00 -07002121
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002122static int
2123 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2124 sd_journal* journal,
2125 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002126{
2127 // Get the Log Entry contents
2128 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002129
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002130 std::string message;
2131 std::string_view syslogID;
2132 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2133 if (ret < 0)
2134 {
2135 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2136 << strerror(-ret);
2137 }
2138 if (!syslogID.empty())
2139 {
2140 message += std::string(syslogID) + ": ";
2141 }
2142
Ed Tanous39e77502019-03-04 17:35:53 -08002143 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002144 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002145 if (ret < 0)
2146 {
2147 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2148 return 1;
2149 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002150 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002151
2152 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002153 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002154 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002155 if (ret < 0)
2156 {
2157 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002158 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002159
2160 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002161 std::string entryTimeStr;
2162 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002163 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002164 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002165 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002166
2167 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07002168 bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
2169 bmcJournalLogEntryJson["@odata.id"] =
2170 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2171 bmcJournalLogEntryID;
2172 bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2173 bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2174 bmcJournalLogEntryJson["Message"] = std::move(message);
2175 bmcJournalLogEntryJson["EntryType"] = "Oem";
2176 bmcJournalLogEntryJson["Severity"] = severity <= 2 ? "Critical"
2177 : severity <= 4 ? "Warning"
2178 : "OK";
2179 bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2180 bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
Jason M. Billse1f26342018-07-18 12:12:00 -07002181 return 0;
2182}
2183
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002184inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002185{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002186 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002187 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002188 .methods(boost::beast::http::verb::get)(
2189 [&app](const crow::Request& req,
2190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2191 query_param::QueryCapabilities capabilities = {
2192 .canDelegateTop = true,
2193 .canDelegateSkip = true,
2194 };
2195 query_param::Query delegatedQuery;
2196 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002197 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002198 {
2199 return;
2200 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07002201
2202 size_t skip = delegatedQuery.skip.value_or(0);
2203 size_t top =
2204 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
2205
Ed Tanous002d39b2022-05-31 08:59:27 -07002206 // Collections don't include the static data added by SubRoute
2207 // because it has a duplicate entry for members
2208 asyncResp->res.jsonValue["@odata.type"] =
2209 "#LogEntryCollection.LogEntryCollection";
2210 asyncResp->res.jsonValue["@odata.id"] =
2211 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2212 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2213 asyncResp->res.jsonValue["Description"] =
2214 "Collection of BMC Journal Entries";
2215 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2216 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002217
Ed Tanous002d39b2022-05-31 08:59:27 -07002218 // Go through the journal and use the timestamp to create a
2219 // unique ID for each entry
2220 sd_journal* journalTmp = nullptr;
2221 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2222 if (ret < 0)
2223 {
2224 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2225 messages::internalError(asyncResp->res);
2226 return;
2227 }
2228 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2229 journalTmp, sd_journal_close);
2230 journalTmp = nullptr;
2231 uint64_t entryCount = 0;
2232 // Reset the unique ID on the first entry
2233 bool firstEntry = true;
2234 SD_JOURNAL_FOREACH(journal.get())
2235 {
2236 entryCount++;
2237 // Handle paging using skip (number of entries to skip from
2238 // the start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07002239 if (entryCount <= skip || entryCount > skip + top)
George Liu0fda0f12021-11-16 10:06:17 +08002240 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002241 continue;
2242 }
2243
2244 std::string idStr;
2245 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2246 {
2247 continue;
2248 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002249 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002250
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002251 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002252 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2253 bmcJournalLogEntry) != 0)
2254 {
George Liu0fda0f12021-11-16 10:06:17 +08002255 messages::internalError(asyncResp->res);
2256 return;
2257 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002258 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002259 }
2260 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07002261 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07002262 {
2263 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2264 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07002265 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07002266 }
George Liu0fda0f12021-11-16 10:06:17 +08002267 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002268}
Jason M. Billse1f26342018-07-18 12:12:00 -07002269
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002270inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002271{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002272 BMCWEB_ROUTE(app,
2273 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002274 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002275 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002276 [&app](const crow::Request& req,
2277 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2278 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002279 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002280 {
2281 return;
2282 }
2283 // Convert the unique ID back to a timestamp to find the entry
2284 uint64_t ts = 0;
2285 uint64_t index = 0;
2286 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2287 {
2288 return;
2289 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002290
Ed Tanous002d39b2022-05-31 08:59:27 -07002291 sd_journal* journalTmp = nullptr;
2292 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2293 if (ret < 0)
2294 {
2295 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2296 messages::internalError(asyncResp->res);
2297 return;
2298 }
2299 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2300 journalTmp, sd_journal_close);
2301 journalTmp = nullptr;
2302 // Go to the timestamp in the log and move to the entry at the
2303 // index tracking the unique ID
2304 std::string idStr;
2305 bool firstEntry = true;
2306 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2307 if (ret < 0)
2308 {
2309 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2310 << strerror(-ret);
2311 messages::internalError(asyncResp->res);
2312 return;
2313 }
2314 for (uint64_t i = 0; i <= index; i++)
2315 {
2316 sd_journal_next(journal.get());
2317 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2318 {
2319 messages::internalError(asyncResp->res);
2320 return;
2321 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002322 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002323 }
2324 // Confirm that the entry ID matches what was requested
2325 if (idStr != entryID)
2326 {
2327 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2328 return;
2329 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002330
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002331 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002332 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002333 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002334 {
2335 messages::internalError(asyncResp->res);
2336 return;
2337 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07002338 asyncResp->res.jsonValue.update(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002339 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002340}
2341
Claire Weinanfdd26902022-03-01 14:18:25 -08002342inline void
2343 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2344 const std::string& dumpType)
2345{
2346 std::string dumpPath;
2347 std::string overWritePolicy;
2348 bool collectDiagnosticDataSupported = false;
2349
2350 if (dumpType == "BMC")
2351 {
2352 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2353 overWritePolicy = "WrapsWhenFull";
2354 collectDiagnosticDataSupported = true;
2355 }
2356 else if (dumpType == "FaultLog")
2357 {
2358 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2359 overWritePolicy = "Unknown";
2360 collectDiagnosticDataSupported = false;
2361 }
2362 else if (dumpType == "System")
2363 {
2364 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2365 overWritePolicy = "WrapsWhenFull";
2366 collectDiagnosticDataSupported = true;
2367 }
2368 else
2369 {
2370 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2371 << dumpType;
2372 messages::internalError(asyncResp->res);
2373 return;
2374 }
2375
2376 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2377 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2378 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2379 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2380 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2381 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2382
2383 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002384 redfish::time_utils::getDateTimeOffsetNow();
Claire Weinanfdd26902022-03-01 14:18:25 -08002385 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2386 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2387 redfishDateTimeOffset.second;
2388
2389 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2390 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2391 dumpPath + "/Actions/LogService.ClearLog";
2392
2393 if (collectDiagnosticDataSupported)
2394 {
2395 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2396 ["target"] =
2397 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2398 }
2399}
2400
2401inline void handleLogServicesDumpServiceGet(
2402 crow::App& app, const std::string& dumpType, const crow::Request& req,
2403 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2404{
2405 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2406 {
2407 return;
2408 }
2409 getDumpServiceInfo(asyncResp, dumpType);
2410}
2411
2412inline void handleLogServicesDumpEntriesCollectionGet(
2413 crow::App& app, const std::string& dumpType, const crow::Request& req,
2414 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2415{
2416 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2417 {
2418 return;
2419 }
2420 getDumpEntryCollection(asyncResp, dumpType);
2421}
2422
2423inline void handleLogServicesDumpEntryGet(
2424 crow::App& app, const std::string& dumpType, const crow::Request& req,
2425 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2426 const std::string& dumpId)
2427{
2428 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2429 {
2430 return;
2431 }
2432 getDumpEntryById(asyncResp, dumpId, dumpType);
2433}
2434
2435inline void handleLogServicesDumpEntryDelete(
2436 crow::App& app, const std::string& dumpType, const crow::Request& req,
2437 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2438 const std::string& dumpId)
2439{
2440 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2441 {
2442 return;
2443 }
2444 deleteDumpEntry(asyncResp, dumpId, dumpType);
2445}
2446
2447inline void handleLogServicesDumpCollectDiagnosticDataPost(
2448 crow::App& app, const std::string& dumpType, const crow::Request& req,
2449 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2450{
2451 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2452 {
2453 return;
2454 }
2455 createDump(asyncResp, req, dumpType);
2456}
2457
2458inline void handleLogServicesDumpClearLogPost(
2459 crow::App& app, const std::string& dumpType, const crow::Request& req,
2460 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2461{
2462 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2463 {
2464 return;
2465 }
2466 clearDump(asyncResp, dumpType);
2467}
2468
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002469inline void requestRoutesBMCDumpService(App& app)
2470{
2471 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002472 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002473 .methods(boost::beast::http::verb::get)(std::bind_front(
2474 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002475}
2476
2477inline void requestRoutesBMCDumpEntryCollection(App& app)
2478{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002479 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002480 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002481 .methods(boost::beast::http::verb::get)(std::bind_front(
2482 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002483}
2484
2485inline void requestRoutesBMCDumpEntry(App& app)
2486{
2487 BMCWEB_ROUTE(app,
2488 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002489 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002490 .methods(boost::beast::http::verb::get)(std::bind_front(
2491 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2492
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002493 BMCWEB_ROUTE(app,
2494 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002495 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002496 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2497 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002498}
2499
2500inline void requestRoutesBMCDumpCreate(App& app)
2501{
George Liu0fda0f12021-11-16 10:06:17 +08002502 BMCWEB_ROUTE(
2503 app,
2504 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002505 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002506 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002507 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2508 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002509}
2510
2511inline void requestRoutesBMCDumpClear(App& app)
2512{
George Liu0fda0f12021-11-16 10:06:17 +08002513 BMCWEB_ROUTE(
2514 app,
2515 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002516 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002517 .methods(boost::beast::http::verb::post)(std::bind_front(
2518 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2519}
2520
2521inline void requestRoutesFaultLogDumpService(App& app)
2522{
2523 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2524 .privileges(redfish::privileges::getLogService)
2525 .methods(boost::beast::http::verb::get)(std::bind_front(
2526 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2527}
2528
2529inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2530{
2531 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2532 .privileges(redfish::privileges::getLogEntryCollection)
2533 .methods(boost::beast::http::verb::get)(
2534 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2535 std::ref(app), "FaultLog"));
2536}
2537
2538inline void requestRoutesFaultLogDumpEntry(App& app)
2539{
2540 BMCWEB_ROUTE(app,
2541 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2542 .privileges(redfish::privileges::getLogEntry)
2543 .methods(boost::beast::http::verb::get)(std::bind_front(
2544 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2545
2546 BMCWEB_ROUTE(app,
2547 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2548 .privileges(redfish::privileges::deleteLogEntry)
2549 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2550 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2551}
2552
2553inline void requestRoutesFaultLogDumpClear(App& app)
2554{
2555 BMCWEB_ROUTE(
2556 app,
2557 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2558 .privileges(redfish::privileges::postLogService)
2559 .methods(boost::beast::http::verb::post)(std::bind_front(
2560 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002561}
2562
2563inline void requestRoutesSystemDumpService(App& app)
2564{
2565 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002566 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002567 .methods(boost::beast::http::verb::get)(
2568 [&app](const crow::Request& req,
2569 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002570 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002571 {
2572 return;
2573 }
2574 asyncResp->res.jsonValue["@odata.id"] =
2575 "/redfish/v1/Systems/system/LogServices/Dump";
2576 asyncResp->res.jsonValue["@odata.type"] =
2577 "#LogService.v1_2_0.LogService";
2578 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2579 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2580 asyncResp->res.jsonValue["Id"] = "Dump";
2581 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302582
Ed Tanous002d39b2022-05-31 08:59:27 -07002583 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002584 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002585 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2586 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2587 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302588
Ed Tanous002d39b2022-05-31 08:59:27 -07002589 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2590 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2591 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2592 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002593
Ed Tanous002d39b2022-05-31 08:59:27 -07002594 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2595 ["target"] =
2596 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002597 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002598}
2599
2600inline void requestRoutesSystemDumpEntryCollection(App& app)
2601{
2602
2603 /**
2604 * Functions triggers appropriate requests on DBus
2605 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002606 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002607 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002608 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002609 [&app](const crow::Request& req,
2610 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002611 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002612 {
2613 return;
2614 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002615 getDumpEntryCollection(asyncResp, "System");
2616 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002617}
2618
2619inline void requestRoutesSystemDumpEntry(App& app)
2620{
2621 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002622 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002623 .privileges(redfish::privileges::getLogEntry)
2624
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002625 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002626 [&app](const crow::Request& req,
2627 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2628 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002629 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002630 {
2631 return;
2632 }
2633 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002634 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002635
2636 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002637 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002638 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002639 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002640 [&app](const crow::Request& req,
2641 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2642 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002643 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002644 {
2645 return;
2646 }
2647 deleteDumpEntry(asyncResp, param, "system");
2648 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002649}
2650
2651inline void requestRoutesSystemDumpCreate(App& app)
2652{
George Liu0fda0f12021-11-16 10:06:17 +08002653 BMCWEB_ROUTE(
2654 app,
2655 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002656 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002657 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002658 [&app](const crow::Request& req,
2659 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002660 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002661 {
2662 return;
2663 }
2664 createDump(asyncResp, req, "System");
2665 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002666}
2667
2668inline void requestRoutesSystemDumpClear(App& app)
2669{
George Liu0fda0f12021-11-16 10:06:17 +08002670 BMCWEB_ROUTE(
2671 app,
2672 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002673 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002674 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002675 [&app](const crow::Request& req,
2676 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002677
Ed Tanous45ca1b82022-03-25 13:07:27 -07002678 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002679 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002680 {
2681 return;
2682 }
2683 clearDump(asyncResp, "System");
2684 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002685}
2686
2687inline void requestRoutesCrashdumpService(App& app)
2688{
2689 // Note: Deviated from redfish privilege registry for GET & HEAD
2690 // method for security reasons.
2691 /**
2692 * Functions triggers appropriate requests on DBus
2693 */
2694 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002695 // This is incorrect, should be:
2696 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002697 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002698 .methods(boost::beast::http::verb::get)(
2699 [&app](const crow::Request& req,
2700 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002701 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002702 {
2703 return;
2704 }
2705 // Copy over the static data to include the entries added by
2706 // SubRoute
2707 asyncResp->res.jsonValue["@odata.id"] =
2708 "/redfish/v1/Systems/system/LogServices/Crashdump";
2709 asyncResp->res.jsonValue["@odata.type"] =
2710 "#LogService.v1_2_0.LogService";
2711 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2712 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2713 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2714 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2715 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302716
Ed Tanous002d39b2022-05-31 08:59:27 -07002717 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002718 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002719 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2720 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2721 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302722
Ed Tanous002d39b2022-05-31 08:59:27 -07002723 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2724 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2725 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2726 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2727 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2728 ["target"] =
2729 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002730 });
2731}
2732
2733void inline requestRoutesCrashdumpClear(App& app)
2734{
George Liu0fda0f12021-11-16 10:06:17 +08002735 BMCWEB_ROUTE(
2736 app,
2737 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002738 // This is incorrect, should be:
2739 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002740 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002741 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002742 [&app](const crow::Request& req,
2743 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002744 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002745 {
2746 return;
2747 }
2748 crow::connections::systemBus->async_method_call(
2749 [asyncResp](const boost::system::error_code ec,
2750 const std::string&) {
2751 if (ec)
2752 {
2753 messages::internalError(asyncResp->res);
2754 return;
2755 }
2756 messages::success(asyncResp->res);
2757 },
2758 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2759 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002760}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002761
zhanghch058d1b46d2021-04-01 11:18:24 +08002762static void
2763 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2764 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002765{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002766 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002767 [asyncResp, logID,
2768 &logEntryJson](const boost::system::error_code ec,
2769 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002770 if (ec)
2771 {
2772 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2773 if (ec.value() ==
2774 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002775 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002776 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002777 }
2778 else
2779 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002780 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002781 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002782 return;
2783 }
2784
2785 std::string timestamp{};
2786 std::string filename{};
2787 std::string logfile{};
2788 parseCrashdumpParameters(params, filename, timestamp, logfile);
2789
2790 if (filename.empty() || timestamp.empty())
2791 {
2792 messages::resourceMissingAtURI(asyncResp->res,
2793 crow::utility::urlFromPieces(logID));
2794 return;
2795 }
2796
2797 std::string crashdumpURI =
2798 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2799 logID + "/" + filename;
Jason M. Bills84afc482022-06-24 12:38:23 -07002800 nlohmann::json::object_t logEntry;
2801 logEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
2802 logEntry["@odata.id"] =
2803 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
2804 logEntry["Name"] = "CPU Crashdump";
2805 logEntry["Id"] = logID;
2806 logEntry["EntryType"] = "Oem";
2807 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2808 logEntry["DiagnosticDataType"] = "OEM";
2809 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2810 logEntry["Created"] = std::move(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07002811
2812 // If logEntryJson references an array of LogEntry resources
2813 // ('Members' list), then push this as a new entry, otherwise set it
2814 // directly
2815 if (logEntryJson.is_array())
2816 {
2817 logEntryJson.push_back(logEntry);
2818 asyncResp->res.jsonValue["Members@odata.count"] =
2819 logEntryJson.size();
2820 }
2821 else
2822 {
Jason M. Billsd405bb52022-06-24 10:52:05 -07002823 logEntryJson.update(logEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002824 }
2825 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002826 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002827 std::move(getStoredLogCallback), crashdumpObject,
2828 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002829 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002830}
2831
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002832inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002833{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002834 // Note: Deviated from redfish privilege registry for GET & HEAD
2835 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002836 /**
2837 * Functions triggers appropriate requests on DBus
2838 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002839 BMCWEB_ROUTE(app,
2840 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002841 // This is incorrect, should be.
2842 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002843 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002844 .methods(boost::beast::http::verb::get)(
2845 [&app](const crow::Request& req,
2846 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002847 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002848 {
2849 return;
2850 }
2851 crow::connections::systemBus->async_method_call(
2852 [asyncResp](const boost::system::error_code ec,
2853 const std::vector<std::string>& resp) {
2854 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002855 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002856 if (ec.value() !=
2857 boost::system::errc::no_such_file_or_directory)
2858 {
2859 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2860 << ec.message();
2861 messages::internalError(asyncResp->res);
2862 return;
2863 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002864 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002865 asyncResp->res.jsonValue["@odata.type"] =
2866 "#LogEntryCollection.LogEntryCollection";
2867 asyncResp->res.jsonValue["@odata.id"] =
2868 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2869 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2870 asyncResp->res.jsonValue["Description"] =
2871 "Collection of Crashdump Entries";
2872 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2873 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002874
Ed Tanous002d39b2022-05-31 08:59:27 -07002875 for (const std::string& path : resp)
2876 {
2877 const sdbusplus::message::object_path objPath(path);
2878 // Get the log ID
2879 std::string logID = objPath.filename();
2880 if (logID.empty())
2881 {
2882 continue;
2883 }
2884 // Add the log entry to the array
2885 logCrashdumpEntry(asyncResp, logID,
2886 asyncResp->res.jsonValue["Members"]);
2887 }
2888 },
2889 "xyz.openbmc_project.ObjectMapper",
2890 "/xyz/openbmc_project/object_mapper",
2891 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2892 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002893 });
2894}
Ed Tanous1da66f72018-07-27 16:13:37 -07002895
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002896inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002897{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002898 // Note: Deviated from redfish privilege registry for GET & HEAD
2899 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002900
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002901 BMCWEB_ROUTE(
2902 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002903 // this is incorrect, should be
2904 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002905 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002906 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002907 [&app](const crow::Request& req,
2908 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2909 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002910 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002911 {
2912 return;
2913 }
2914 const std::string& logID = param;
2915 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2916 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002917}
Ed Tanous1da66f72018-07-27 16:13:37 -07002918
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002919inline void requestRoutesCrashdumpFile(App& app)
2920{
2921 // Note: Deviated from redfish privilege registry for GET & HEAD
2922 // method for security reasons.
2923 BMCWEB_ROUTE(
2924 app,
2925 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002926 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002927 .methods(boost::beast::http::verb::get)(
Nan Zhoua4ce1142022-08-02 18:45:25 +00002928 [](const crow::Request& req,
2929 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2930 const std::string& logID, const std::string& fileName) {
Shounak Mitra2a9beee2022-07-20 18:41:30 +00002931 // Do not call getRedfishRoute here since the crashdump file is not a
2932 // Redfish resource.
Ed Tanous002d39b2022-05-31 08:59:27 -07002933 auto getStoredLogCallback =
2934 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2935 const boost::system::error_code ec,
2936 const std::vector<
2937 std::pair<std::string, dbus::utility::DbusVariantType>>&
2938 resp) {
2939 if (ec)
2940 {
2941 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2942 messages::internalError(asyncResp->res);
2943 return;
2944 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002945
Ed Tanous002d39b2022-05-31 08:59:27 -07002946 std::string dbusFilename{};
2947 std::string dbusTimestamp{};
2948 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002949
Ed Tanous002d39b2022-05-31 08:59:27 -07002950 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2951 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002952
Ed Tanous002d39b2022-05-31 08:59:27 -07002953 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2954 dbusFilepath.empty())
2955 {
2956 messages::resourceMissingAtURI(asyncResp->res, url);
2957 return;
2958 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002959
Ed Tanous002d39b2022-05-31 08:59:27 -07002960 // Verify the file name parameter is correct
2961 if (fileName != dbusFilename)
2962 {
2963 messages::resourceMissingAtURI(asyncResp->res, url);
2964 return;
2965 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002966
Ed Tanous002d39b2022-05-31 08:59:27 -07002967 if (!std::filesystem::exists(dbusFilepath))
2968 {
2969 messages::resourceMissingAtURI(asyncResp->res, url);
2970 return;
2971 }
2972 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2973 asyncResp->res.body() =
2974 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002975
Ed Tanous002d39b2022-05-31 08:59:27 -07002976 // Configure this to be a file download when accessed
2977 // from a browser
Ed Tanousd9f6c622022-03-17 09:12:17 -07002978 asyncResp->res.addHeader(
2979 boost::beast::http::field::content_disposition, "attachment");
Ed Tanous002d39b2022-05-31 08:59:27 -07002980 };
2981 crow::connections::systemBus->async_method_call(
2982 std::move(getStoredLogCallback), crashdumpObject,
2983 crashdumpPath + std::string("/") + logID,
2984 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2985 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002986}
2987
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002988enum class OEMDiagnosticType
2989{
2990 onDemand,
2991 telemetry,
2992 invalid,
2993};
2994
Ed Tanousf7725d72022-03-07 12:46:00 -08002995inline OEMDiagnosticType
2996 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002997{
2998 if (oemDiagStr == "OnDemand")
2999 {
3000 return OEMDiagnosticType::onDemand;
3001 }
3002 if (oemDiagStr == "Telemetry")
3003 {
3004 return OEMDiagnosticType::telemetry;
3005 }
3006
3007 return OEMDiagnosticType::invalid;
3008}
3009
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003010inline void requestRoutesCrashdumpCollect(App& app)
3011{
3012 // Note: Deviated from redfish privilege registry for GET & HEAD
3013 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08003014 BMCWEB_ROUTE(
3015 app,
3016 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07003017 // The below is incorrect; Should be ConfigureManager
3018 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003019 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07003020 .methods(boost::beast::http::verb::post)(
3021 [&app](const crow::Request& req,
3022 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003023 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003024 {
3025 return;
3026 }
3027 std::string diagnosticDataType;
3028 std::string oemDiagnosticDataType;
3029 if (!redfish::json_util::readJsonAction(
3030 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3031 "OEMDiagnosticDataType", oemDiagnosticDataType))
3032 {
3033 return;
3034 }
3035
3036 if (diagnosticDataType != "OEM")
3037 {
3038 BMCWEB_LOG_ERROR
3039 << "Only OEM DiagnosticDataType supported for Crashdump";
3040 messages::actionParameterValueFormatError(
3041 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3042 "CollectDiagnosticData");
3043 return;
3044 }
3045
3046 OEMDiagnosticType oemDiagType =
3047 getOEMDiagnosticType(oemDiagnosticDataType);
3048
3049 std::string iface;
3050 std::string method;
3051 std::string taskMatchStr;
3052 if (oemDiagType == OEMDiagnosticType::onDemand)
3053 {
3054 iface = crashdumpOnDemandInterface;
3055 method = "GenerateOnDemandLog";
3056 taskMatchStr = "type='signal',"
3057 "interface='org.freedesktop.DBus.Properties',"
3058 "member='PropertiesChanged',"
3059 "arg0namespace='com.intel.crashdump'";
3060 }
3061 else if (oemDiagType == OEMDiagnosticType::telemetry)
3062 {
3063 iface = crashdumpTelemetryInterface;
3064 method = "GenerateTelemetryLog";
3065 taskMatchStr = "type='signal',"
3066 "interface='org.freedesktop.DBus.Properties',"
3067 "member='PropertiesChanged',"
3068 "arg0namespace='com.intel.crashdump'";
3069 }
3070 else
3071 {
3072 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3073 << oemDiagnosticDataType;
3074 messages::actionParameterValueFormatError(
3075 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3076 "CollectDiagnosticData");
3077 return;
3078 }
3079
3080 auto collectCrashdumpCallback =
3081 [asyncResp, payload(task::Payload(req)),
3082 taskMatchStr](const boost::system::error_code ec,
3083 const std::string&) mutable {
3084 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003085 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003086 if (ec.value() == boost::system::errc::operation_not_supported)
3087 {
3088 messages::resourceInStandby(asyncResp->res);
3089 }
3090 else if (ec.value() ==
3091 boost::system::errc::device_or_resource_busy)
3092 {
3093 messages::serviceTemporarilyUnavailable(asyncResp->res,
3094 "60");
3095 }
3096 else
3097 {
3098 messages::internalError(asyncResp->res);
3099 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003100 return;
3101 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003102 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -05003103 [](boost::system::error_code err, sdbusplus::message_t&,
Ed Tanous002d39b2022-05-31 08:59:27 -07003104 const std::shared_ptr<task::TaskData>& taskData) {
3105 if (!err)
3106 {
3107 taskData->messages.emplace_back(messages::taskCompletedOK(
3108 std::to_string(taskData->index)));
3109 taskData->state = "Completed";
3110 }
3111 return task::completed;
3112 },
3113 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003114
Ed Tanous002d39b2022-05-31 08:59:27 -07003115 task->startTimer(std::chrono::minutes(5));
3116 task->populateResp(asyncResp->res);
3117 task->payload.emplace(std::move(payload));
3118 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003119
Ed Tanous002d39b2022-05-31 08:59:27 -07003120 crow::connections::systemBus->async_method_call(
3121 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3122 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003123 });
3124}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003125
Andrew Geisslercb92c032018-08-17 07:56:14 -07003126/**
3127 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3128 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003129inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003130{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003131 /**
3132 * Function handles POST method request.
3133 * The Clear Log actions does not require any parameter.The action deletes
3134 * all entries found in the Entries collection for this Log Service.
3135 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003136
George Liu0fda0f12021-11-16 10:06:17 +08003137 BMCWEB_ROUTE(
3138 app,
3139 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003140 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003141 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003142 [&app](const crow::Request& req,
3143 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003144 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003145 {
3146 return;
3147 }
3148 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003149
Ed Tanous002d39b2022-05-31 08:59:27 -07003150 // Process response from Logging service.
3151 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3152 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3153 if (ec)
3154 {
3155 // TODO Handle for specific error code
3156 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3157 asyncResp->res.result(
3158 boost::beast::http::status::internal_server_error);
3159 return;
3160 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003161
Ed Tanous002d39b2022-05-31 08:59:27 -07003162 asyncResp->res.result(boost::beast::http::status::no_content);
3163 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003164
Ed Tanous002d39b2022-05-31 08:59:27 -07003165 // Make call to Logging service to request Clear Log
3166 crow::connections::systemBus->async_method_call(
3167 respHandler, "xyz.openbmc_project.Logging",
3168 "/xyz/openbmc_project/logging",
3169 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3170 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003171}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003172
3173/****************************************************
3174 * Redfish PostCode interfaces
3175 * using DBUS interface: getPostCodesTS
3176 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003177inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003178{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003179 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003180 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003181 .methods(boost::beast::http::verb::get)(
3182 [&app](const crow::Request& req,
3183 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003184 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003185 {
3186 return;
3187 }
Ed Tanous14766872022-03-15 10:44:42 -07003188
Ed Tanous002d39b2022-05-31 08:59:27 -07003189 asyncResp->res.jsonValue["@odata.id"] =
3190 "/redfish/v1/Systems/system/LogServices/PostCodes";
3191 asyncResp->res.jsonValue["@odata.type"] =
3192 "#LogService.v1_1_0.LogService";
3193 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3194 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3195 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3196 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3197 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3198 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303199
Ed Tanous002d39b2022-05-31 08:59:27 -07003200 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07003201 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07003202 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3203 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3204 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303205
Ed Tanous002d39b2022-05-31 08:59:27 -07003206 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3207 {"target",
3208 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003209 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003210}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003211
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003212inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003213{
George Liu0fda0f12021-11-16 10:06:17 +08003214 BMCWEB_ROUTE(
3215 app,
3216 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003217 // The following privilege is incorrect; It should be ConfigureManager
3218 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003219 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003220 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003221 [&app](const crow::Request& req,
3222 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003223 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003224 {
3225 return;
3226 }
3227 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003228
Ed Tanous002d39b2022-05-31 08:59:27 -07003229 // Make call to post-code service to request clear all
3230 crow::connections::systemBus->async_method_call(
3231 [asyncResp](const boost::system::error_code ec) {
3232 if (ec)
3233 {
3234 // TODO Handle for specific error code
3235 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3236 << ec;
3237 asyncResp->res.result(
3238 boost::beast::http::status::internal_server_error);
3239 messages::internalError(asyncResp->res);
3240 return;
3241 }
3242 },
3243 "xyz.openbmc_project.State.Boot.PostCode0",
3244 "/xyz/openbmc_project/State/Boot/PostCode0",
3245 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3246 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003247}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003248
3249static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003250 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303251 const boost::container::flat_map<
3252 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003253 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3254 const uint64_t skip = 0, const uint64_t top = 0)
3255{
3256 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003257 const registries::Message* message =
3258 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003259
3260 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003261 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003262
3263 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303264 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3265 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003266 {
3267 currentCodeIndex++;
3268 std::string postcodeEntryID =
3269 "B" + std::to_string(bootIndex) + "-" +
3270 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3271
3272 uint64_t usecSinceEpoch = code.first;
3273 uint64_t usTimeOffset = 0;
3274
3275 if (1 == currentCodeIndex)
3276 { // already incremented
3277 firstCodeTimeUs = code.first;
3278 }
3279 else
3280 {
3281 usTimeOffset = code.first - firstCodeTimeUs;
3282 }
3283
3284 // skip if no specific codeIndex is specified and currentCodeIndex does
3285 // not fall between top and skip
3286 if ((codeIndex == 0) &&
3287 (currentCodeIndex <= skip || currentCodeIndex > top))
3288 {
3289 continue;
3290 }
3291
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003292 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003293 // currentIndex
3294 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3295 {
3296 // This is done for simplicity. 1st entry is needed to calculate
3297 // time offset. To improve efficiency, one can get to the entry
3298 // directly (possibly with flatmap's nth method)
3299 continue;
3300 }
3301
3302 // currentCodeIndex is within top and skip or equal to specified code
3303 // index
3304
3305 // Get the Created time from the timestamp
3306 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003307 entryTimeStr =
Ed Tanous2b829372022-08-03 14:22:34 -07003308 redfish::time_utils::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003309
3310 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3311 std::ostringstream hexCode;
3312 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303313 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003314 std::ostringstream timeOffsetStr;
3315 // Set Fixed -Point Notation
3316 timeOffsetStr << std::fixed;
3317 // Set precision to 4 digits
3318 timeOffsetStr << std::setprecision(4);
3319 // Add double to stream
3320 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3321 std::vector<std::string> messageArgs = {
3322 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3323
3324 // Get MessageArgs template from message registry
3325 std::string msg;
3326 if (message != nullptr)
3327 {
3328 msg = message->message;
3329
3330 // fill in this post code value
3331 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003332 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003333 {
3334 std::string argStr = "%" + std::to_string(++i);
3335 size_t argPos = msg.find(argStr);
3336 if (argPos != std::string::npos)
3337 {
3338 msg.replace(argPos, argStr.length(), messageArg);
3339 }
3340 }
3341 }
3342
Tim Leed4342a92020-04-27 11:47:58 +08003343 // Get Severity template from message registry
3344 std::string severity;
3345 if (message != nullptr)
3346 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003347 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003348 }
3349
ZhikuiRena3316fc2020-01-29 14:58:08 -08003350 // add to AsyncResp
3351 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003352 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills84afc482022-06-24 12:38:23 -07003353 bmcLogEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
3354 bmcLogEntry["@odata.id"] =
3355 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3356 postcodeEntryID;
3357 bmcLogEntry["Name"] = "POST Code Log Entry";
3358 bmcLogEntry["Id"] = postcodeEntryID;
3359 bmcLogEntry["Message"] = std::move(msg);
3360 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3361 bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3362 bmcLogEntry["EntryType"] = "Event";
3363 bmcLogEntry["Severity"] = std::move(severity);
3364 bmcLogEntry["Created"] = entryTimeStr;
George Liu647b3cd2021-07-05 12:43:56 +08003365 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3366 {
3367 bmcLogEntry["AdditionalDataURI"] =
3368 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3369 postcodeEntryID + "/attachment";
3370 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003371 }
3372}
3373
zhanghch058d1b46d2021-04-01 11:18:24 +08003374static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003375 const uint16_t bootIndex,
3376 const uint64_t codeIndex)
3377{
3378 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303379 [aResp, bootIndex,
3380 codeIndex](const boost::system::error_code ec,
3381 const boost::container::flat_map<
3382 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3383 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003384 if (ec)
3385 {
3386 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3387 messages::internalError(aResp->res);
3388 return;
3389 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003390
Ed Tanous002d39b2022-05-31 08:59:27 -07003391 // skip the empty postcode boots
3392 if (postcode.empty())
3393 {
3394 return;
3395 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003396
Ed Tanous002d39b2022-05-31 08:59:27 -07003397 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003398
Ed Tanous002d39b2022-05-31 08:59:27 -07003399 aResp->res.jsonValue["Members@odata.count"] =
3400 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003401 },
Jonathan Doman15124762021-01-07 17:54:17 -08003402 "xyz.openbmc_project.State.Boot.PostCode0",
3403 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003404 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3405 bootIndex);
3406}
3407
zhanghch058d1b46d2021-04-01 11:18:24 +08003408static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003409 const uint16_t bootIndex,
3410 const uint16_t bootCount,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003411 const uint64_t entryCount, size_t skip,
3412 size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003413{
3414 crow::connections::systemBus->async_method_call(
3415 [aResp, bootIndex, bootCount, entryCount, skip,
3416 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303417 const boost::container::flat_map<
3418 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3419 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003420 if (ec)
3421 {
3422 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3423 messages::internalError(aResp->res);
3424 return;
3425 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003426
Ed Tanous002d39b2022-05-31 08:59:27 -07003427 uint64_t endCount = entryCount;
3428 if (!postcode.empty())
3429 {
3430 endCount = entryCount + postcode.size();
Ed Tanous3648c8b2022-07-25 13:39:59 -07003431 if (skip < endCount && (top + skip) > entryCount)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003432 {
Ed Tanous3648c8b2022-07-25 13:39:59 -07003433 uint64_t thisBootSkip =
3434 std::max(static_cast<uint64_t>(skip), entryCount) -
3435 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003436 uint64_t thisBootTop =
Ed Tanous3648c8b2022-07-25 13:39:59 -07003437 std::min(static_cast<uint64_t>(top + skip), endCount) -
3438 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003439
3440 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3441 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003442 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003443 aResp->res.jsonValue["Members@odata.count"] = endCount;
3444 }
3445
3446 // continue to previous bootIndex
3447 if (bootIndex < bootCount)
3448 {
3449 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3450 bootCount, endCount, skip, top);
3451 }
Jiaqing Zhao81584ab2022-07-28 00:33:45 +08003452 else if (skip + top < endCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07003453 {
3454 aResp->res.jsonValue["Members@odata.nextLink"] =
3455 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3456 std::to_string(skip + top);
3457 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003458 },
Jonathan Doman15124762021-01-07 17:54:17 -08003459 "xyz.openbmc_project.State.Boot.PostCode0",
3460 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003461 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3462 bootIndex);
3463}
3464
zhanghch058d1b46d2021-04-01 11:18:24 +08003465static void
3466 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003467 size_t skip, size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003468{
3469 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003470 sdbusplus::asio::getProperty<uint16_t>(
3471 *crow::connections::systemBus,
3472 "xyz.openbmc_project.State.Boot.PostCode0",
3473 "/xyz/openbmc_project/State/Boot/PostCode0",
3474 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3475 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3476 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003477 if (ec)
3478 {
3479 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3480 messages::internalError(aResp->res);
3481 return;
3482 }
3483 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003484 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003485}
3486
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003487inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003488{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003489 BMCWEB_ROUTE(app,
3490 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003491 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003492 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003493 [&app](const crow::Request& req,
3494 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003495 query_param::QueryCapabilities capabilities = {
3496 .canDelegateTop = true,
3497 .canDelegateSkip = true,
3498 };
3499 query_param::Query delegatedQuery;
3500 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003501 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003502 {
3503 return;
3504 }
3505 asyncResp->res.jsonValue["@odata.type"] =
3506 "#LogEntryCollection.LogEntryCollection";
3507 asyncResp->res.jsonValue["@odata.id"] =
3508 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3509 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3510 asyncResp->res.jsonValue["Description"] =
3511 "Collection of POST Code Log Entries";
3512 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3513 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07003514 size_t skip = delegatedQuery.skip.value_or(0);
3515 size_t top =
3516 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
3517 getCurrentBootNumber(asyncResp, skip, top);
Ed Tanous002d39b2022-05-31 08:59:27 -07003518 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003519}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003520
George Liu647b3cd2021-07-05 12:43:56 +08003521/**
3522 * @brief Parse post code ID and get the current value and index value
3523 * eg: postCodeID=B1-2, currentValue=1, index=2
3524 *
3525 * @param[in] postCodeID Post Code ID
3526 * @param[out] currentValue Current value
3527 * @param[out] index Index value
3528 *
3529 * @return bool true if the parsing is successful, false the parsing fails
3530 */
3531inline static bool parsePostCode(const std::string& postCodeID,
3532 uint64_t& currentValue, uint16_t& index)
3533{
3534 std::vector<std::string> split;
3535 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3536 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3537 {
3538 return false;
3539 }
3540
Ed Tanousca45aa32022-01-07 09:28:45 -08003541 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003542 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003543 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003544 const char* end = split[0].data() + split[0].size();
3545 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3546
3547 if (ptrIndex != end || ecIndex != std::errc())
3548 {
3549 return false;
3550 }
3551
3552 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003553
3554 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003555 end = split[1].data() + split[1].size();
3556 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003557
Tony Lee517d9a52022-06-28 15:41:23 +08003558 return ptrValue == end && ecValue == std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003559}
3560
3561inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3562{
George Liu0fda0f12021-11-16 10:06:17 +08003563 BMCWEB_ROUTE(
3564 app,
3565 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003566 .privileges(redfish::privileges::getLogEntry)
3567 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003568 [&app](const crow::Request& req,
3569 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3570 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003571 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003572 {
3573 return;
3574 }
3575 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3576 {
3577 asyncResp->res.result(boost::beast::http::status::bad_request);
3578 return;
3579 }
George Liu647b3cd2021-07-05 12:43:56 +08003580
Ed Tanous002d39b2022-05-31 08:59:27 -07003581 uint64_t currentValue = 0;
3582 uint16_t index = 0;
3583 if (!parsePostCode(postCodeID, currentValue, index))
3584 {
3585 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3586 return;
3587 }
George Liu647b3cd2021-07-05 12:43:56 +08003588
Ed Tanous002d39b2022-05-31 08:59:27 -07003589 crow::connections::systemBus->async_method_call(
3590 [asyncResp, postCodeID, currentValue](
3591 const boost::system::error_code ec,
3592 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3593 postcodes) {
3594 if (ec.value() == EBADR)
3595 {
3596 messages::resourceNotFound(asyncResp->res, "LogEntry",
3597 postCodeID);
3598 return;
3599 }
3600 if (ec)
3601 {
3602 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3603 messages::internalError(asyncResp->res);
3604 return;
3605 }
George Liu647b3cd2021-07-05 12:43:56 +08003606
Ed Tanous002d39b2022-05-31 08:59:27 -07003607 size_t value = static_cast<size_t>(currentValue) - 1;
3608 if (value == std::string::npos || postcodes.size() < currentValue)
3609 {
3610 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3611 messages::resourceNotFound(asyncResp->res, "LogEntry",
3612 postCodeID);
3613 return;
3614 }
George Liu647b3cd2021-07-05 12:43:56 +08003615
Ed Tanous002d39b2022-05-31 08:59:27 -07003616 const auto& [tID, c] = postcodes[value];
3617 if (c.empty())
3618 {
3619 BMCWEB_LOG_INFO << "No found post code data";
3620 messages::resourceNotFound(asyncResp->res, "LogEntry",
3621 postCodeID);
3622 return;
3623 }
3624 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3625 const char* d = reinterpret_cast<const char*>(c.data());
3626 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003627
Ed Tanousd9f6c622022-03-17 09:12:17 -07003628 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07003629 "application/octet-stream");
Ed Tanousd9f6c622022-03-17 09:12:17 -07003630 asyncResp->res.addHeader(
3631 boost::beast::http::field::content_transfer_encoding, "Base64");
Ed Tanous002d39b2022-05-31 08:59:27 -07003632 asyncResp->res.body() = crow::utility::base64encode(strData);
3633 },
3634 "xyz.openbmc_project.State.Boot.PostCode0",
3635 "/xyz/openbmc_project/State/Boot/PostCode0",
3636 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3637 });
George Liu647b3cd2021-07-05 12:43:56 +08003638}
3639
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003640inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003641{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003642 BMCWEB_ROUTE(
3643 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003644 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003645 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003646 [&app](const crow::Request& req,
3647 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3648 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003649 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003650 {
3651 return;
3652 }
3653 uint16_t bootIndex = 0;
3654 uint64_t codeIndex = 0;
3655 if (!parsePostCode(targetID, codeIndex, bootIndex))
3656 {
3657 // Requested ID was not found
3658 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3659 return;
3660 }
3661 if (bootIndex == 0 || codeIndex == 0)
3662 {
3663 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3664 << targetID;
3665 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003666
Ed Tanous002d39b2022-05-31 08:59:27 -07003667 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3668 asyncResp->res.jsonValue["@odata.id"] =
3669 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3670 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3671 asyncResp->res.jsonValue["Description"] =
3672 "Collection of POST Code Log Entries";
3673 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3674 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003675
Ed Tanous002d39b2022-05-31 08:59:27 -07003676 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3677 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003678}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003679
Ed Tanous1da66f72018-07-27 16:13:37 -07003680} // namespace redfish