blob: 2a26ab9e669e346a2817baec2ba79316c3c07fac [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>
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +020041#include <sdbusplus/asio/property.hpp>
42#include <sdbusplus/unpack_properties.hpp>
43#include <utils/dbus_utils.hpp>
Ed Tanous2b829372022-08-03 14:22:34 -070044#include <utils/time_utils.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050045
George Liu647b3cd2021-07-05 12:43:56 +080046#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070047#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080048#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070049#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070050#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080051#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070052
53namespace redfish
54{
55
Gunnar Mills1214b7e2020-06-04 10:11:30 -050056constexpr char const* crashdumpObject = "com.intel.crashdump";
57constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050058constexpr char const* crashdumpInterface = "com.intel.crashdump";
59constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070060 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050061constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070062 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070063constexpr char const* crashdumpTelemetryInterface =
64 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070065
Ed Tanousfffb8c12022-02-07 23:53:03 -080066namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -070067{
Ed Tanous26702d02021-11-03 15:02:33 -070068static const Message*
69 getMessageFromRegistry(const std::string& messageKey,
70 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070071{
Ed Tanous002d39b2022-05-31 08:59:27 -070072 std::span<const MessageEntry>::iterator messageIt =
73 std::find_if(registry.begin(), registry.end(),
74 [&messageKey](const MessageEntry& messageEntry) {
75 return std::strcmp(messageEntry.first, messageKey.c_str()) == 0;
Ed Tanous26702d02021-11-03 15:02:33 -070076 });
77 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070078 {
79 return &messageIt->second;
80 }
81
82 return nullptr;
83}
84
Gunnar Mills1214b7e2020-06-04 10:11:30 -050085static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070086{
87 // Redfish MessageIds are in the form
88 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
89 // the right Message
90 std::vector<std::string> fields;
91 fields.reserve(4);
92 boost::split(fields, messageID, boost::is_any_of("."));
Ed Tanous02cad962022-06-30 16:50:15 -070093 const std::string& registryName = fields[0];
94 const std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070095
96 // Find the right registry and check it for the MessageKey
97 if (std::string(base::header.registryPrefix) == registryName)
98 {
99 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -0700100 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -0700101 }
102 if (std::string(openbmc::header.registryPrefix) == registryName)
103 {
104 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -0700105 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -0700106 }
107 return nullptr;
108}
Ed Tanousfffb8c12022-02-07 23:53:03 -0800109} // namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -0700110
James Feistf6150402019-01-08 10:36:20 -0800111namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700112
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500113inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700114{
Ed Tanousd4d25792020-09-29 15:15:03 -0700115 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
116 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
117 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
118 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700119 {
120 return "Critical";
121 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700122 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
123 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
124 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700125 {
126 return "OK";
127 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700128 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700129 {
130 return "Warning";
131 }
132 return "";
133}
134
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700135inline static int getJournalMetadata(sd_journal* journal,
136 const std::string_view& field,
137 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700138{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500139 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700140 size_t length = 0;
141 int ret = 0;
142 // Get the metadata from the requested field of the journal entry
Ed Tanous46ff87b2022-01-07 09:25:51 -0800143 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
144 const void** dataVoid = reinterpret_cast<const void**>(&data);
145
146 ret = sd_journal_get_data(journal, field.data(), dataVoid, &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700147 if (ret < 0)
148 {
149 return ret;
150 }
Ed Tanous39e77502019-03-04 17:35:53 -0800151 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700152 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000153 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700154 return ret;
155}
156
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700157inline static int getJournalMetadata(sd_journal* journal,
158 const std::string_view& field,
159 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700160{
161 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800162 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700163 // Get the metadata from the requested field of the journal entry
164 ret = getJournalMetadata(journal, field, metadata);
165 if (ret < 0)
166 {
167 return ret;
168 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000169 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700170 return ret;
171}
172
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700173inline static bool getEntryTimestamp(sd_journal* journal,
174 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800175{
176 int ret = 0;
177 uint64_t timestamp = 0;
178 ret = sd_journal_get_realtime_usec(journal, &timestamp);
179 if (ret < 0)
180 {
181 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
182 << strerror(-ret);
183 return false;
184 }
Ed Tanous2b829372022-08-03 14:22:34 -0700185 entryTimestamp =
186 redfish::time_utils::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500187 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800188}
Ed Tanous50b8a432022-02-03 16:29:50 -0800189
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700190inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
191 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700192{
193 int ret = 0;
194 static uint64_t prevTs = 0;
195 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700196 if (firstEntry)
197 {
198 prevTs = 0;
199 }
200
Jason M. Bills16428a12018-11-02 12:42:29 -0700201 // Get the entry timestamp
202 uint64_t curTs = 0;
203 ret = sd_journal_get_realtime_usec(journal, &curTs);
204 if (ret < 0)
205 {
206 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
207 << strerror(-ret);
208 return false;
209 }
210 // If the timestamp isn't unique, increment the index
211 if (curTs == prevTs)
212 {
213 index++;
214 }
215 else
216 {
217 // Otherwise, reset it
218 index = 0;
219 }
220 // Save the timestamp
221 prevTs = curTs;
222
223 entryID = std::to_string(curTs);
224 if (index > 0)
225 {
226 entryID += "_" + std::to_string(index);
227 }
228 return true;
229}
230
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500231static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700232 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700233{
Ed Tanous271584a2019-07-09 16:24:22 -0700234 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700235 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700236 if (firstEntry)
237 {
238 prevTs = 0;
239 }
240
Jason M. Bills95820182019-04-22 16:25:34 -0700241 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700242 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700243 std::tm timeStruct = {};
244 std::istringstream entryStream(logEntry);
245 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
246 {
247 curTs = std::mktime(&timeStruct);
248 }
249 // If the timestamp isn't unique, increment the index
250 if (curTs == prevTs)
251 {
252 index++;
253 }
254 else
255 {
256 // Otherwise, reset it
257 index = 0;
258 }
259 // Save the timestamp
260 prevTs = curTs;
261
262 entryID = std::to_string(curTs);
263 if (index > 0)
264 {
265 entryID += "_" + std::to_string(index);
266 }
267 return true;
268}
269
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700270inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800271 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
272 const std::string& entryID, uint64_t& timestamp,
273 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700274{
275 if (entryID.empty())
276 {
277 return false;
278 }
279 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800280 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700281
Ed Tanous81ce6092020-12-17 16:54:55 +0000282 auto underscorePos = tsStr.find('_');
Ed Tanous71d5d8d2022-01-25 11:04:33 -0800283 if (underscorePos != std::string_view::npos)
Jason M. Bills16428a12018-11-02 12:42:29 -0700284 {
285 // Timestamp has an index
286 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800287 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700288 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700289 auto [ptr, ec] = std::from_chars(
290 indexStr.data(), indexStr.data() + indexStr.size(), index);
291 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700292 {
Ed Tanousace85d62021-10-26 12:45:59 -0700293 messages::resourceMissingAtURI(
294 asyncResp->res, crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700295 return false;
296 }
297 }
298 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700299 auto [ptr, ec] =
300 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
301 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700302 {
Ed Tanousace85d62021-10-26 12:45:59 -0700303 messages::resourceMissingAtURI(asyncResp->res,
304 crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700305 return false;
306 }
307 return true;
308}
309
Jason M. Bills95820182019-04-22 16:25:34 -0700310static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500311 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700312{
313 static const std::filesystem::path redfishLogDir = "/var/log";
314 static const std::string redfishLogFilename = "redfish";
315
316 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500317 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700318 std::filesystem::directory_iterator(redfishLogDir))
319 {
320 // If we find a redfish log file, save the path
321 std::string filename = dirEnt.path().filename();
Ed Tanous11ba3972022-07-11 09:50:41 -0700322 if (filename.starts_with(redfishLogFilename))
Jason M. Bills95820182019-04-22 16:25:34 -0700323 {
324 redfishLogFiles.emplace_back(redfishLogDir / filename);
325 }
326 }
327 // As the log files rotate, they are appended with a ".#" that is higher for
328 // the older logs. Since we don't expect more than 10 log files, we
329 // can just sort the list to get them in order from newest to oldest
330 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
331
332 return !redfishLogFiles.empty();
333}
334
Claire Weinanaefe3782022-07-15 19:17:19 -0700335inline void parseDumpEntryFromDbusObject(
Jiaqing Zhao2d613eb2022-08-15 16:03:00 +0800336 const dbus::utility::ManagedObjectType::value_type& object,
Claire Weinanc6fecda2022-07-15 10:43:25 -0700337 std::string& dumpStatus, uint64_t& size, uint64_t& timestampUs,
Claire Weinanaefe3782022-07-15 19:17:19 -0700338 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
339{
340 for (const auto& interfaceMap : object.second)
341 {
342 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
343 {
344 for (const auto& propertyMap : interfaceMap.second)
345 {
346 if (propertyMap.first == "Status")
347 {
348 const auto* status =
349 std::get_if<std::string>(&propertyMap.second);
350 if (status == nullptr)
351 {
352 messages::internalError(asyncResp->res);
353 break;
354 }
355 dumpStatus = *status;
356 }
357 }
358 }
359 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
360 {
361 for (const auto& propertyMap : interfaceMap.second)
362 {
363 if (propertyMap.first == "Size")
364 {
365 const auto* sizePtr =
366 std::get_if<uint64_t>(&propertyMap.second);
367 if (sizePtr == nullptr)
368 {
369 messages::internalError(asyncResp->res);
370 break;
371 }
372 size = *sizePtr;
373 break;
374 }
375 }
376 }
377 else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime")
378 {
379 for (const auto& propertyMap : interfaceMap.second)
380 {
381 if (propertyMap.first == "Elapsed")
382 {
383 const uint64_t* usecsTimeStamp =
384 std::get_if<uint64_t>(&propertyMap.second);
385 if (usecsTimeStamp == nullptr)
386 {
387 messages::internalError(asyncResp->res);
388 break;
389 }
Claire Weinanc6fecda2022-07-15 10:43:25 -0700390 timestampUs = *usecsTimeStamp;
Claire Weinanaefe3782022-07-15 19:17:19 -0700391 break;
392 }
393 }
394 }
395 }
396}
397
Nan Zhou21ab4042022-06-26 23:07:40 +0000398static std::string getDumpEntriesPath(const std::string& dumpType)
Claire Weinanfdd26902022-03-01 14:18:25 -0800399{
400 std::string entriesPath;
401
402 if (dumpType == "BMC")
403 {
404 entriesPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
405 }
406 else if (dumpType == "FaultLog")
407 {
408 entriesPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/";
409 }
410 else if (dumpType == "System")
411 {
412 entriesPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
413 }
414 else
415 {
416 BMCWEB_LOG_ERROR << "getDumpEntriesPath() invalid dump type: "
417 << dumpType;
418 }
419
420 // Returns empty string on error
421 return entriesPath;
422}
423
zhanghch058d1b46d2021-04-01 11:18:24 +0800424inline void
425 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
426 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500427{
Claire Weinanfdd26902022-03-01 14:18:25 -0800428 std::string entriesPath = getDumpEntriesPath(dumpType);
429 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500430 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500431 messages::internalError(asyncResp->res);
432 return;
433 }
434
435 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800436 [asyncResp, entriesPath,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800437 dumpType](const boost::system::error_code ec,
438 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700439 if (ec)
440 {
441 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
442 messages::internalError(asyncResp->res);
443 return;
444 }
445
Claire Weinanfdd26902022-03-01 14:18:25 -0800446 // Remove ending slash
447 std::string odataIdStr = entriesPath;
448 if (!odataIdStr.empty())
449 {
450 odataIdStr.pop_back();
451 }
452
453 asyncResp->res.jsonValue["@odata.type"] =
454 "#LogEntryCollection.LogEntryCollection";
455 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
456 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
457 asyncResp->res.jsonValue["Description"] =
458 "Collection of " + dumpType + " Dump Entries";
459
Ed Tanous002d39b2022-05-31 08:59:27 -0700460 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
461 entriesArray = nlohmann::json::array();
462 std::string dumpEntryPath =
463 "/xyz/openbmc_project/dump/" +
464 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
465
466 std::sort(resp.begin(), resp.end(), [](const auto& l, const auto& r) {
467 return AlphanumLess<std::string>()(l.first.filename(),
468 r.first.filename());
469 });
470
471 for (auto& object : resp)
472 {
473 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500474 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700475 continue;
476 }
Claire Weinanc6fecda2022-07-15 10:43:25 -0700477 uint64_t timestampUs = 0;
Ed Tanous002d39b2022-05-31 08:59:27 -0700478 uint64_t size = 0;
479 std::string dumpStatus;
Jason M. Bills433b68b2022-06-28 12:24:26 -0700480 nlohmann::json::object_t thisEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -0700481
482 std::string entryID = object.first.filename();
483 if (entryID.empty())
484 {
485 continue;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500486 }
487
Claire Weinanc6fecda2022-07-15 10:43:25 -0700488 parseDumpEntryFromDbusObject(object, dumpStatus, size, timestampUs,
Claire Weinanaefe3782022-07-15 19:17:19 -0700489 asyncResp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700490
491 if (dumpStatus !=
492 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
493 !dumpStatus.empty())
494 {
495 // Dump status is not Complete, no need to enumerate
496 continue;
497 }
498
499 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800500 thisEntry["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700501 thisEntry["Id"] = entryID;
502 thisEntry["EntryType"] = "Event";
Ed Tanous002d39b2022-05-31 08:59:27 -0700503 thisEntry["Name"] = dumpType + " Dump Entry";
504
Ed Tanous002d39b2022-05-31 08:59:27 -0700505 if (dumpType == "BMC")
506 {
Claire Weinanc6fecda2022-07-15 10:43:25 -0700507 thisEntry["Created"] = redfish::time_utils::getDateTimeUint(
508 timestampUs / 1000 / 1000);
Ed Tanous002d39b2022-05-31 08:59:27 -0700509 thisEntry["DiagnosticDataType"] = "Manager";
510 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800511 entriesPath + entryID + "/attachment";
512 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700513 }
Claire Weinanc6fecda2022-07-15 10:43:25 -0700514 else if (dumpType == "FaultLog")
515 {
516 thisEntry["Created"] =
517 redfish::time_utils::getDateTimeUintUs(timestampUs);
518 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700519 else if (dumpType == "System")
520 {
Claire Weinanc6fecda2022-07-15 10:43:25 -0700521 thisEntry["Created"] = redfish::time_utils::getDateTimeUint(
522 timestampUs / 1000 / 1000);
Ed Tanous002d39b2022-05-31 08:59:27 -0700523 thisEntry["DiagnosticDataType"] = "OEM";
524 thisEntry["OEMDiagnosticDataType"] = "System";
525 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800526 entriesPath + entryID + "/attachment";
527 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700528 }
529 entriesArray.push_back(std::move(thisEntry));
530 }
531 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500532 },
533 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
534 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
535}
536
zhanghch058d1b46d2021-04-01 11:18:24 +0800537inline void
Claire Weinanc7a6d662022-06-13 16:36:39 -0700538 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800539 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500540{
Claire Weinanfdd26902022-03-01 14:18:25 -0800541 std::string entriesPath = getDumpEntriesPath(dumpType);
542 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500543 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500544 messages::internalError(asyncResp->res);
545 return;
546 }
547
548 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800549 [asyncResp, entryID, dumpType,
550 entriesPath](const boost::system::error_code ec,
Ed Tanous02cad962022-06-30 16:50:15 -0700551 const dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700552 if (ec)
553 {
554 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
555 messages::internalError(asyncResp->res);
556 return;
557 }
558
559 bool foundDumpEntry = false;
560 std::string dumpEntryPath =
561 "/xyz/openbmc_project/dump/" +
562 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
563
564 for (const auto& objectPath : resp)
565 {
566 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500567 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700568 continue;
569 }
570
571 foundDumpEntry = true;
Claire Weinanc6fecda2022-07-15 10:43:25 -0700572 uint64_t timestampUs = 0;
Ed Tanous002d39b2022-05-31 08:59:27 -0700573 uint64_t size = 0;
574 std::string dumpStatus;
575
Claire Weinanaefe3782022-07-15 19:17:19 -0700576 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size,
Claire Weinanc6fecda2022-07-15 10:43:25 -0700577 timestampUs, asyncResp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700578
579 if (dumpStatus !=
580 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
581 !dumpStatus.empty())
582 {
583 // Dump status is not Complete
584 // return not found until status is changed to Completed
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200585 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
586 entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500587 return;
588 }
589
Ed Tanous002d39b2022-05-31 08:59:27 -0700590 asyncResp->res.jsonValue["@odata.type"] =
591 "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800592 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700593 asyncResp->res.jsonValue["Id"] = entryID;
594 asyncResp->res.jsonValue["EntryType"] = "Event";
Ed Tanous002d39b2022-05-31 08:59:27 -0700595 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500596
Ed Tanous002d39b2022-05-31 08:59:27 -0700597 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500598 {
Claire Weinanc6fecda2022-07-15 10:43:25 -0700599 asyncResp->res.jsonValue["Created"] =
600 redfish::time_utils::getDateTimeUint(timestampUs / 1000 /
601 1000);
Ed Tanous002d39b2022-05-31 08:59:27 -0700602 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
603 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800604 entriesPath + entryID + "/attachment";
605 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500606 }
Claire Weinanc6fecda2022-07-15 10:43:25 -0700607 else if (dumpType == "FaultLog")
608 {
609 asyncResp->res.jsonValue["Created"] =
610 redfish::time_utils::getDateTimeUintUs(timestampUs);
611 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700612 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500613 {
Claire Weinanc6fecda2022-07-15 10:43:25 -0700614 asyncResp->res.jsonValue["Created"] =
615 redfish::time_utils::getDateTimeUint(timestampUs / 1000 /
616 1000);
Ed Tanous002d39b2022-05-31 08:59:27 -0700617 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
618 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
619 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800620 entriesPath + entryID + "/attachment";
621 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500622 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700623 }
624 if (!foundDumpEntry)
625 {
626 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
627 messages::internalError(asyncResp->res);
628 return;
629 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500630 },
631 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
632 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
633}
634
zhanghch058d1b46d2021-04-01 11:18:24 +0800635inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800636 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500637 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500638{
Ed Tanous002d39b2022-05-31 08:59:27 -0700639 auto respHandler =
640 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500641 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
642 if (ec)
643 {
George Liu3de8d8b2021-03-22 17:49:39 +0800644 if (ec.value() == EBADR)
645 {
646 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
647 return;
648 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500649 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -0800650 << ec << " entryID=" << entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500651 messages::internalError(asyncResp->res);
652 return;
653 }
654 };
655 crow::connections::systemBus->async_method_call(
656 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500657 "/xyz/openbmc_project/dump/" +
658 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
659 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500660 "xyz.openbmc_project.Object.Delete", "Delete");
661}
662
zhanghch058d1b46d2021-04-01 11:18:24 +0800663inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700664 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800665 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
666 const uint32_t& dumpId, const std::string& dumpPath,
667 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500668{
669 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -0500670 [dumpId, dumpPath,
671 dumpType](boost::system::error_code err, sdbusplus::message_t& m,
672 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700673 if (err)
674 {
675 BMCWEB_LOG_ERROR << "Error in creating a dump";
676 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500677 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700678 }
679
680 dbus::utility::DBusInteracesMap interfacesList;
681
682 sdbusplus::message::object_path objPath;
683
684 m.read(objPath, interfacesList);
685
686 if (objPath.str ==
687 "/xyz/openbmc_project/dump/" +
688 std::string(boost::algorithm::to_lower_copy(dumpType)) +
689 "/entry/" + std::to_string(dumpId))
690 {
691 nlohmann::json retMessage = messages::success();
692 taskData->messages.emplace_back(retMessage);
693
694 std::string headerLoc =
695 "Location: " + dumpPath + std::to_string(dumpId);
696 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
697
698 taskData->state = "Completed";
699 return task::completed;
700 }
701 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500702 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800703 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500704 "member='InterfacesAdded', "
705 "path='/xyz/openbmc_project/dump'");
706
707 task->startTimer(std::chrono::minutes(3));
708 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700709 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500710}
711
zhanghch058d1b46d2021-04-01 11:18:24 +0800712inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
713 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500714{
Claire Weinanfdd26902022-03-01 14:18:25 -0800715 std::string dumpPath = getDumpEntriesPath(dumpType);
716 if (dumpPath.empty())
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500717 {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500718 messages::internalError(asyncResp->res);
719 return;
720 }
721
722 std::optional<std::string> diagnosticDataType;
723 std::optional<std::string> oemDiagnosticDataType;
724
Willy Tu15ed6782021-12-14 11:03:16 -0800725 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500726 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
727 "OEMDiagnosticDataType", oemDiagnosticDataType))
728 {
729 return;
730 }
731
732 if (dumpType == "System")
733 {
734 if (!oemDiagnosticDataType || !diagnosticDataType)
735 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800736 BMCWEB_LOG_ERROR
737 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500738 messages::actionParameterMissing(
739 asyncResp->res, "CollectDiagnosticData",
740 "DiagnosticDataType & OEMDiagnosticDataType");
741 return;
742 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700743 if ((*oemDiagnosticDataType != "System") ||
744 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500745 {
746 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
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/Systems/system/LogServices/Dump/";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500751 }
752 else if (dumpType == "BMC")
753 {
754 if (!diagnosticDataType)
755 {
George Liu0fda0f12021-11-16 10:06:17 +0800756 BMCWEB_LOG_ERROR
757 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500758 messages::actionParameterMissing(
759 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
760 return;
761 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700762 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500763 {
764 BMCWEB_LOG_ERROR
765 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700766 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500767 return;
768 }
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500769 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/";
770 }
771 else
772 {
773 BMCWEB_LOG_ERROR << "CreateDump failed. Unknown dump type";
774 messages::internalError(asyncResp->res);
775 return;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500776 }
777
778 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700779 [asyncResp, payload(task::Payload(req)), dumpPath,
780 dumpType](const boost::system::error_code ec,
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500781 const sdbusplus::message::message& msg,
Ed Tanous98be3e32021-09-16 15:05:36 -0700782 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700783 if (ec)
784 {
785 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500786 const sd_bus_error* dbusError = msg.get_error();
787 if (dbusError == nullptr)
788 {
789 messages::internalError(asyncResp->res);
790 return;
791 }
792
793 BMCWEB_LOG_ERROR << "CreateDump DBus error: " << dbusError->name
794 << " and error msg: " << dbusError->message;
795 if (std::string_view(
796 "xyz.openbmc_project.Common.Error.NotAllowed") ==
797 dbusError->name)
798 {
799 messages::resourceInStandby(asyncResp->res);
800 return;
801 }
802 if (std::string_view(
803 "xyz.openbmc_project.Dump.Create.Error.Disabled") ==
804 dbusError->name)
805 {
806 messages::serviceDisabled(asyncResp->res, dumpPath);
807 return;
808 }
809 if (std::string_view(
810 "xyz.openbmc_project.Common.Error.Unavailable") ==
811 dbusError->name)
812 {
813 messages::resourceInUse(asyncResp->res);
814 return;
815 }
816 // Other Dbus errors such as:
817 // xyz.openbmc_project.Common.Error.InvalidArgument &
818 // org.freedesktop.DBus.Error.InvalidArgs are all related to
819 // the dbus call that is made here in the bmcweb
820 // implementation and has nothing to do with the client's
821 // input in the request. Hence, returning internal error
822 // back to the client.
Ed Tanous002d39b2022-05-31 08:59:27 -0700823 messages::internalError(asyncResp->res);
824 return;
825 }
826 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500827
Ed Tanous002d39b2022-05-31 08:59:27 -0700828 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
829 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500830 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500831 "xyz.openbmc_project.Dump.Manager",
832 "/xyz/openbmc_project/dump/" +
833 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500834 "xyz.openbmc_project.Dump.Create", "CreateDump");
835}
836
zhanghch058d1b46d2021-04-01 11:18:24 +0800837inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
838 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500839{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500840 std::string dumpTypeLowerCopy =
841 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800842
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500843 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800844 [asyncResp, dumpType](
845 const boost::system::error_code ec,
846 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700847 if (ec)
848 {
849 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
850 messages::internalError(asyncResp->res);
851 return;
852 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500853
Ed Tanous002d39b2022-05-31 08:59:27 -0700854 for (const std::string& path : subTreePaths)
855 {
856 sdbusplus::message::object_path objPath(path);
857 std::string logID = objPath.filename();
858 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500859 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700860 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500861 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700862 deleteDumpEntry(asyncResp, logID, dumpType);
863 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500864 },
865 "xyz.openbmc_project.ObjectMapper",
866 "/xyz/openbmc_project/object_mapper",
867 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500868 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
869 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
870 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500871}
872
Ed Tanousb9d36b42022-02-26 21:42:46 -0800873inline static void
874 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
875 std::string& filename, std::string& timestamp,
876 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700877{
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200878 const std::string* filenamePtr = nullptr;
879 const std::string* timestampPtr = nullptr;
880 const std::string* logfilePtr = nullptr;
881
882 const bool success = sdbusplus::unpackPropertiesNoThrow(
883 dbus_utils::UnpackErrorPrinter(), params, "Timestamp", timestampPtr,
884 "Filename", filenamePtr, "Log", logfilePtr);
885
886 if (!success)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700887 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +0200888 return;
889 }
890
891 if (filenamePtr != nullptr)
892 {
893 filename = *filenamePtr;
894 }
895
896 if (timestampPtr != nullptr)
897 {
898 timestamp = *timestampPtr;
899 }
900
901 if (logfilePtr != nullptr)
902 {
903 logfile = *logfilePtr;
Johnathan Mantey043a0532020-03-10 17:15:28 -0700904 }
905}
906
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500907constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700908inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700909{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800910 /**
911 * Functions triggers appropriate requests on DBus
912 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700913 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700914 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700915 .methods(boost::beast::http::verb::get)(
916 [&app](const crow::Request& req,
917 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000918 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700919 {
920 return;
921 }
922 // Collections don't include the static data added by SubRoute
923 // because it has a duplicate entry for members
924 asyncResp->res.jsonValue["@odata.type"] =
925 "#LogServiceCollection.LogServiceCollection";
926 asyncResp->res.jsonValue["@odata.id"] =
927 "/redfish/v1/Systems/system/LogServices";
928 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
929 asyncResp->res.jsonValue["Description"] =
930 "Collection of LogServices for this Computer System";
931 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
932 logServiceArray = nlohmann::json::array();
933 nlohmann::json::object_t eventLog;
934 eventLog["@odata.id"] =
935 "/redfish/v1/Systems/system/LogServices/EventLog";
936 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500937#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700938 nlohmann::json::object_t dumpLog;
939 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
940 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600941#endif
942
Jason M. Billsd53dd412019-02-12 17:16:22 -0800943#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700944 nlohmann::json::object_t crashdump;
945 crashdump["@odata.id"] =
946 "/redfish/v1/Systems/system/LogServices/Crashdump";
947 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800948#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800949
950#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700951 nlohmann::json::object_t hostlogger;
952 hostlogger["@odata.id"] =
953 "/redfish/v1/Systems/system/LogServices/HostLogger";
954 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800955#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700956 asyncResp->res.jsonValue["Members@odata.count"] =
957 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800958
Ed Tanous002d39b2022-05-31 08:59:27 -0700959 crow::connections::systemBus->async_method_call(
960 [asyncResp](const boost::system::error_code ec,
961 const dbus::utility::MapperGetSubTreePathsResponse&
962 subtreePath) {
963 if (ec)
964 {
965 BMCWEB_LOG_ERROR << ec;
966 return;
967 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700968
Ed Tanous002d39b2022-05-31 08:59:27 -0700969 for (const auto& pathStr : subtreePath)
970 {
971 if (pathStr.find("PostCode") != std::string::npos)
972 {
973 nlohmann::json& logServiceArrayLocal =
974 asyncResp->res.jsonValue["Members"];
Ed Tanous613dabe2022-07-09 11:17:36 -0700975 nlohmann::json::object_t member;
976 member["@odata.id"] =
977 "/redfish/v1/Systems/system/LogServices/PostCodes";
978
979 logServiceArrayLocal.push_back(std::move(member));
980
Ed Tanous002d39b2022-05-31 08:59:27 -0700981 asyncResp->res.jsonValue["Members@odata.count"] =
982 logServiceArrayLocal.size();
983 return;
984 }
985 }
986 },
987 "xyz.openbmc_project.ObjectMapper",
988 "/xyz/openbmc_project/object_mapper",
989 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
990 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700991 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700992}
993
994inline void requestRoutesEventLogService(App& app)
995{
996 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700997 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700998 .methods(boost::beast::http::verb::get)(
999 [&app](const crow::Request& req,
1000 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001001 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001002 {
1003 return;
1004 }
1005 asyncResp->res.jsonValue["@odata.id"] =
1006 "/redfish/v1/Systems/system/LogServices/EventLog";
1007 asyncResp->res.jsonValue["@odata.type"] =
1008 "#LogService.v1_1_0.LogService";
1009 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1010 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
1011 asyncResp->res.jsonValue["Id"] = "EventLog";
1012 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301013
Ed Tanous002d39b2022-05-31 08:59:27 -07001014 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07001015 redfish::time_utils::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +05301016
Ed Tanous002d39b2022-05-31 08:59:27 -07001017 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1018 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1019 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05301020
Ed Tanous002d39b2022-05-31 08:59:27 -07001021 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1022 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1023 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001024
Ed Tanous002d39b2022-05-31 08:59:27 -07001025 {"target",
1026 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001027 });
1028}
1029
1030inline void requestRoutesJournalEventLogClear(App& app)
1031{
Jason M. Bills4978b632022-02-22 14:17:43 -08001032 BMCWEB_ROUTE(
1033 app,
1034 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001035 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001036 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001037 [&app](const crow::Request& req,
1038 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001039 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001040 {
1041 return;
1042 }
1043 // Clear the EventLog by deleting the log files
1044 std::vector<std::filesystem::path> redfishLogFiles;
1045 if (getRedfishLogFiles(redfishLogFiles))
1046 {
1047 for (const std::filesystem::path& file : redfishLogFiles)
1048 {
1049 std::error_code ec;
1050 std::filesystem::remove(file, ec);
1051 }
1052 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001053
Ed Tanous002d39b2022-05-31 08:59:27 -07001054 // Reload rsyslog so it knows to start new log files
1055 crow::connections::systemBus->async_method_call(
1056 [asyncResp](const boost::system::error_code ec) {
1057 if (ec)
1058 {
1059 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1060 messages::internalError(asyncResp->res);
1061 return;
1062 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001063
Ed Tanous002d39b2022-05-31 08:59:27 -07001064 messages::success(asyncResp->res);
1065 },
1066 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1067 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1068 "replace");
1069 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001070}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001071
Jason M. Billsac992cd2022-06-24 13:31:46 -07001072enum class LogParseError
1073{
1074 success,
1075 parseFailed,
1076 messageIdNotInRegistry,
1077};
1078
1079static LogParseError
1080 fillEventLogEntryJson(const std::string& logEntryID,
1081 const std::string& logEntry,
1082 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001083{
Jason M. Bills95820182019-04-22 16:25:34 -07001084 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001085 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001086 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001087 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001088 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001089 return LogParseError::parseFailed;
Jason M. Bills95820182019-04-22 16:25:34 -07001090 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001091 std::string timestamp = logEntry.substr(0, space);
1092 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001093 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001094 if (entryStart == std::string::npos)
1095 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001096 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001097 }
1098 std::string_view entry(logEntry);
1099 entry.remove_prefix(entryStart);
1100 // Use split to separate the entry into its fields
1101 std::vector<std::string> logEntryFields;
1102 boost::split(logEntryFields, entry, boost::is_any_of(","),
1103 boost::token_compress_on);
1104 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001105 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001106 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001107 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001108 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001109 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001110
Jason M. Bills4851d452019-03-28 11:27:48 -07001111 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001112 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001113
Sui Chen54417b02022-03-24 14:59:52 -07001114 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001115 {
Sui Chen54417b02022-03-24 14:59:52 -07001116 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001117 return LogParseError::messageIdNotInRegistry;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001118 }
1119
Sui Chen54417b02022-03-24 14:59:52 -07001120 std::string msg = message->message;
1121
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001122 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001123 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001124 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001125 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001126 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001127 // If the first string is empty, assume there are no MessageArgs
1128 std::size_t messageArgsSize = 0;
1129 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001130 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001131 messageArgsSize = logEntryFields.size() - 1;
1132 }
1133
Ed Tanous23a21a12020-07-25 04:45:05 +00001134 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001135
1136 // Fill the MessageArgs into the Message
1137 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001138 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001139 {
1140 std::string argStr = "%" + std::to_string(++i);
1141 size_t argPos = msg.find(argStr);
1142 if (argPos != std::string::npos)
1143 {
1144 msg.replace(argPos, argStr.length(), messageArg);
1145 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001146 }
1147 }
1148
Jason M. Bills95820182019-04-22 16:25:34 -07001149 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1150 // format which matches the Redfish format except for the fractional seconds
1151 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001152 std::size_t dot = timestamp.find_first_of('.');
1153 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001154 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001155 {
Jason M. Bills95820182019-04-22 16:25:34 -07001156 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001157 }
1158
1159 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07001160 logEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1161 logEntryJson["@odata.id"] =
1162 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1163 logEntryJson["Name"] = "System Event Log Entry";
1164 logEntryJson["Id"] = logEntryID;
1165 logEntryJson["Message"] = std::move(msg);
1166 logEntryJson["MessageId"] = std::move(messageID);
1167 logEntryJson["MessageArgs"] = messageArgs;
1168 logEntryJson["EntryType"] = "Event";
1169 logEntryJson["Severity"] = message->messageSeverity;
1170 logEntryJson["Created"] = std::move(timestamp);
Jason M. Billsac992cd2022-06-24 13:31:46 -07001171 return LogParseError::success;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001172}
1173
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001174inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001175{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001176 BMCWEB_ROUTE(app,
1177 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001178 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001179 .methods(boost::beast::http::verb::get)(
1180 [&app](const crow::Request& req,
1181 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1182 query_param::QueryCapabilities capabilities = {
1183 .canDelegateTop = true,
1184 .canDelegateSkip = true,
1185 };
1186 query_param::Query delegatedQuery;
1187 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001188 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001189 {
1190 return;
1191 }
Jiaqing Zhao5143f7a2022-07-22 09:33:33 +08001192 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
Ed Tanous3648c8b2022-07-25 13:39:59 -07001193 size_t skip = delegatedQuery.skip.value_or(0);
1194
Ed Tanous002d39b2022-05-31 08:59:27 -07001195 // Collections don't include the static data added by SubRoute
1196 // because it has a duplicate entry for members
1197 asyncResp->res.jsonValue["@odata.type"] =
1198 "#LogEntryCollection.LogEntryCollection";
1199 asyncResp->res.jsonValue["@odata.id"] =
1200 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1201 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1202 asyncResp->res.jsonValue["Description"] =
1203 "Collection of System Event Log Entries";
1204
1205 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1206 logEntryArray = nlohmann::json::array();
1207 // Go through the log files and create a unique ID for each
1208 // entry
1209 std::vector<std::filesystem::path> redfishLogFiles;
1210 getRedfishLogFiles(redfishLogFiles);
1211 uint64_t entryCount = 0;
1212 std::string logEntry;
1213
1214 // Oldest logs are in the last file, so start there and loop
1215 // backwards
1216 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1217 it++)
1218 {
1219 std::ifstream logStream(*it);
1220 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001221 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001222 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001223 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001224
Ed Tanous002d39b2022-05-31 08:59:27 -07001225 // Reset the unique ID on the first entry
1226 bool firstEntry = true;
1227 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001228 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001229 std::string idStr;
1230 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001231 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001232 continue;
1233 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001234 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001235
Jason M. Billsde703c52022-06-23 14:19:04 -07001236 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001237 LogParseError status =
1238 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1239 if (status == LogParseError::messageIdNotInRegistry)
1240 {
1241 continue;
1242 }
1243 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001244 {
1245 messages::internalError(asyncResp->res);
1246 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001247 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001248
Jason M. Billsde703c52022-06-23 14:19:04 -07001249 entryCount++;
1250 // Handle paging using skip (number of entries to skip from the
1251 // start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07001252 if (entryCount <= skip || entryCount > skip + top)
Jason M. Billsde703c52022-06-23 14:19:04 -07001253 {
1254 continue;
1255 }
1256
1257 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001258 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001259 }
1260 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001261 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07001262 {
1263 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1264 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001265 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07001266 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001267 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001268}
Chicago Duan336e96c2019-07-15 14:22:08 +08001269
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001270inline void requestRoutesJournalEventLogEntry(App& app)
1271{
1272 BMCWEB_ROUTE(
1273 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001274 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001275 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001276 [&app](const crow::Request& req,
1277 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1278 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001279 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001280 {
1281 return;
1282 }
1283 const std::string& targetID = param;
1284
1285 // Go through the log files and check the unique ID for each
1286 // entry to find the target entry
1287 std::vector<std::filesystem::path> redfishLogFiles;
1288 getRedfishLogFiles(redfishLogFiles);
1289 std::string logEntry;
1290
1291 // Oldest logs are in the last file, so start there and loop
1292 // backwards
1293 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1294 it++)
1295 {
1296 std::ifstream logStream(*it);
1297 if (!logStream.is_open())
1298 {
1299 continue;
1300 }
1301
1302 // Reset the unique ID on the first entry
1303 bool firstEntry = true;
1304 while (std::getline(logStream, logEntry))
1305 {
1306 std::string idStr;
1307 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001308 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001309 continue;
1310 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001311 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001312
1313 if (idStr == targetID)
1314 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001315 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001316 LogParseError status =
1317 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1318 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001319 {
1320 messages::internalError(asyncResp->res);
1321 return;
1322 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07001323 asyncResp->res.jsonValue.update(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001324 return;
1325 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001326 }
1327 }
1328 // Requested ID was not found
1329 messages::resourceMissingAtURI(asyncResp->res,
1330 crow::utility::urlFromPieces(targetID));
1331 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001332}
1333
1334inline void requestRoutesDBusEventLogEntryCollection(App& app)
1335{
1336 BMCWEB_ROUTE(app,
1337 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001338 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001339 .methods(boost::beast::http::verb::get)(
1340 [&app](const crow::Request& req,
1341 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001342 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001343 {
1344 return;
1345 }
1346 // Collections don't include the static data added by SubRoute
1347 // because it has a duplicate entry for members
1348 asyncResp->res.jsonValue["@odata.type"] =
1349 "#LogEntryCollection.LogEntryCollection";
1350 asyncResp->res.jsonValue["@odata.id"] =
1351 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1352 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1353 asyncResp->res.jsonValue["Description"] =
1354 "Collection of System Event Log Entries";
1355
1356 // DBus implementation of EventLog/Entries
1357 // Make call to Logging Service to find all log entry objects
1358 crow::connections::systemBus->async_method_call(
1359 [asyncResp](const boost::system::error_code ec,
1360 const dbus::utility::ManagedObjectType& resp) {
1361 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001362 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001363 // TODO Handle for specific error code
1364 BMCWEB_LOG_ERROR
1365 << "getLogEntriesIfaceData resp_handler got error " << ec;
1366 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001367 return;
1368 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001369 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1370 entriesArray = nlohmann::json::array();
1371 for (const auto& objectPath : resp)
1372 {
1373 const uint32_t* id = nullptr;
1374 const uint64_t* timestamp = nullptr;
1375 const uint64_t* updateTimestamp = nullptr;
1376 const std::string* severity = nullptr;
1377 const std::string* message = nullptr;
1378 const std::string* filePath = nullptr;
1379 bool resolved = false;
1380 for (const auto& interfaceMap : objectPath.second)
1381 {
1382 if (interfaceMap.first ==
1383 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001384 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001385 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001386 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001387 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001388 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001389 id = std::get_if<uint32_t>(&propertyMap.second);
1390 }
1391 else if (propertyMap.first == "Timestamp")
1392 {
1393 timestamp =
1394 std::get_if<uint64_t>(&propertyMap.second);
1395 }
1396 else if (propertyMap.first == "UpdateTimestamp")
1397 {
1398 updateTimestamp =
1399 std::get_if<uint64_t>(&propertyMap.second);
1400 }
1401 else if (propertyMap.first == "Severity")
1402 {
1403 severity = std::get_if<std::string>(
1404 &propertyMap.second);
1405 }
1406 else if (propertyMap.first == "Message")
1407 {
1408 message = std::get_if<std::string>(
1409 &propertyMap.second);
1410 }
1411 else if (propertyMap.first == "Resolved")
1412 {
1413 const bool* resolveptr =
1414 std::get_if<bool>(&propertyMap.second);
1415 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001416 {
1417 messages::internalError(asyncResp->res);
1418 return;
1419 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001420 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001421 }
1422 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001423 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001424 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001425 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001426 messages::internalError(asyncResp->res);
1427 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001428 }
1429 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001430 else if (interfaceMap.first ==
1431 "xyz.openbmc_project.Common.FilePath")
1432 {
1433 for (const auto& propertyMap : interfaceMap.second)
1434 {
1435 if (propertyMap.first == "Path")
1436 {
1437 filePath = std::get_if<std::string>(
1438 &propertyMap.second);
1439 }
1440 }
1441 }
1442 }
1443 // Object path without the
1444 // xyz.openbmc_project.Logging.Entry interface, ignore
1445 // and continue.
1446 if (id == nullptr || message == nullptr ||
1447 severity == nullptr || timestamp == nullptr ||
1448 updateTimestamp == nullptr)
1449 {
1450 continue;
1451 }
1452 entriesArray.push_back({});
1453 nlohmann::json& thisEntry = entriesArray.back();
1454 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1455 thisEntry["@odata.id"] =
1456 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1457 std::to_string(*id);
1458 thisEntry["Name"] = "System Event Log Entry";
1459 thisEntry["Id"] = std::to_string(*id);
1460 thisEntry["Message"] = *message;
1461 thisEntry["Resolved"] = resolved;
1462 thisEntry["EntryType"] = "Event";
1463 thisEntry["Severity"] =
1464 translateSeverityDbusToRedfish(*severity);
1465 thisEntry["Created"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001466 redfish::time_utils::getDateTimeUintMs(*timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001467 thisEntry["Modified"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001468 redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001469 if (filePath != nullptr)
1470 {
1471 thisEntry["AdditionalDataURI"] =
1472 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1473 std::to_string(*id) + "/attachment";
1474 }
1475 }
1476 std::sort(
1477 entriesArray.begin(), entriesArray.end(),
1478 [](const nlohmann::json& left, const nlohmann::json& right) {
1479 return (left["Id"] <= right["Id"]);
1480 });
1481 asyncResp->res.jsonValue["Members@odata.count"] =
1482 entriesArray.size();
1483 },
1484 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1485 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001486 });
1487}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001488
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001489inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001490{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001491 BMCWEB_ROUTE(
1492 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001493 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001494 .methods(boost::beast::http::verb::get)(
1495 [&app](const crow::Request& req,
1496 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1497 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001498 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001499 {
1500 return;
1501 }
1502 std::string entryID = param;
1503 dbus::utility::escapePathForDbus(entryID);
1504
1505 // DBus implementation of EventLog/Entries
1506 // Make call to Logging Service to find all log entry objects
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001507 sdbusplus::asio::getAllProperties(
1508 *crow::connections::systemBus, "xyz.openbmc_project.Logging",
1509 "/xyz/openbmc_project/logging/entry/" + entryID, "",
Ed Tanous002d39b2022-05-31 08:59:27 -07001510 [asyncResp, entryID](const boost::system::error_code ec,
1511 const dbus::utility::DBusPropertiesMap& resp) {
1512 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001513 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001514 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1515 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001516 return;
1517 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001518 if (ec)
1519 {
1520 BMCWEB_LOG_ERROR
1521 << "EventLogEntry (DBus) resp_handler got error " << ec;
1522 messages::internalError(asyncResp->res);
1523 return;
1524 }
1525 const uint32_t* id = nullptr;
1526 const uint64_t* timestamp = nullptr;
1527 const uint64_t* updateTimestamp = nullptr;
1528 const std::string* severity = nullptr;
1529 const std::string* message = nullptr;
1530 const std::string* filePath = nullptr;
1531 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001532
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001533 const bool success = sdbusplus::unpackPropertiesNoThrow(
1534 dbus_utils::UnpackErrorPrinter(), resp, "Id", id, "Timestamp",
1535 timestamp, "UpdateTimestamp", updateTimestamp, "Severity",
1536 severity, "Message", message, "Resolved", resolved, "Path",
1537 filePath);
1538
1539 if (!success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001540 {
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001541 messages::internalError(asyncResp->res);
1542 return;
Ed Tanous002d39b2022-05-31 08:59:27 -07001543 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001544
Ed Tanous002d39b2022-05-31 08:59:27 -07001545 if (id == nullptr || message == nullptr || severity == nullptr ||
1546 timestamp == nullptr || updateTimestamp == nullptr)
1547 {
1548 messages::internalError(asyncResp->res);
1549 return;
1550 }
1551 asyncResp->res.jsonValue["@odata.type"] =
1552 "#LogEntry.v1_8_0.LogEntry";
1553 asyncResp->res.jsonValue["@odata.id"] =
1554 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1555 std::to_string(*id);
1556 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1557 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1558 asyncResp->res.jsonValue["Message"] = *message;
1559 asyncResp->res.jsonValue["Resolved"] = resolved;
1560 asyncResp->res.jsonValue["EntryType"] = "Event";
1561 asyncResp->res.jsonValue["Severity"] =
1562 translateSeverityDbusToRedfish(*severity);
1563 asyncResp->res.jsonValue["Created"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001564 redfish::time_utils::getDateTimeUintMs(*timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001565 asyncResp->res.jsonValue["Modified"] =
Ed Tanous2b829372022-08-03 14:22:34 -07001566 redfish::time_utils::getDateTimeUintMs(*updateTimestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07001567 if (filePath != nullptr)
1568 {
1569 asyncResp->res.jsonValue["AdditionalDataURI"] =
1570 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1571 std::to_string(*id) + "/attachment";
1572 }
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02001573 });
Ed Tanous45ca1b82022-03-25 13:07:27 -07001574 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001575
1576 BMCWEB_ROUTE(
1577 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001578 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001579 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001580 [&app](const crow::Request& req,
1581 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1582 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001583 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001584 {
1585 return;
1586 }
1587 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001588
Ed Tanous002d39b2022-05-31 08:59:27 -07001589 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1590 resolved))
1591 {
1592 return;
1593 }
1594 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001595
Ed Tanous002d39b2022-05-31 08:59:27 -07001596 crow::connections::systemBus->async_method_call(
1597 [asyncResp, entryId](const boost::system::error_code ec) {
1598 if (ec)
1599 {
1600 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1601 messages::internalError(asyncResp->res);
1602 return;
1603 }
1604 },
1605 "xyz.openbmc_project.Logging",
1606 "/xyz/openbmc_project/logging/entry/" + entryId,
1607 "org.freedesktop.DBus.Properties", "Set",
1608 "xyz.openbmc_project.Logging.Entry", "Resolved",
1609 dbus::utility::DbusVariantType(*resolved));
1610 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001611
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001612 BMCWEB_ROUTE(
1613 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001614 .privileges(redfish::privileges::deleteLogEntry)
1615
Ed Tanous002d39b2022-05-31 08:59:27 -07001616 .methods(boost::beast::http::verb::delete_)(
1617 [&app](const crow::Request& req,
1618 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1619 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001620 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001621 {
1622 return;
1623 }
1624 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1625
1626 std::string entryID = param;
1627
1628 dbus::utility::escapePathForDbus(entryID);
1629
1630 // Process response from Logging service.
1631 auto respHandler =
1632 [asyncResp, entryID](const boost::system::error_code ec) {
1633 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1634 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001635 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001636 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001637 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001638 messages::resourceNotFound(asyncResp->res, "LogEntry",
1639 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001640 return;
1641 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001642 // TODO Handle for specific error code
1643 BMCWEB_LOG_ERROR
1644 << "EventLogEntry (DBus) doDelete respHandler got error "
1645 << ec;
1646 asyncResp->res.result(
1647 boost::beast::http::status::internal_server_error);
1648 return;
1649 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001650
Ed Tanous002d39b2022-05-31 08:59:27 -07001651 asyncResp->res.result(boost::beast::http::status::ok);
1652 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001653
Ed Tanous002d39b2022-05-31 08:59:27 -07001654 // Make call to Logging service to request Delete Log
1655 crow::connections::systemBus->async_method_call(
1656 respHandler, "xyz.openbmc_project.Logging",
1657 "/xyz/openbmc_project/logging/entry/" + entryID,
1658 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001659 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660}
1661
1662inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001663{
George Liu0fda0f12021-11-16 10:06:17 +08001664 BMCWEB_ROUTE(
1665 app,
1666 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001667 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001668 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001669 [&app](const crow::Request& req,
1670 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1671 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001672 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001673 {
1674 return;
1675 }
Ed Tanous99351cd2022-08-07 16:42:51 -07001676 if (http_helpers::isContentTypeAllowed(
1677 req.getHeaderValue("Accept"),
1678 http_helpers::ContentType::OctetStream))
Ed Tanous002d39b2022-05-31 08:59:27 -07001679 {
1680 asyncResp->res.result(boost::beast::http::status::bad_request);
1681 return;
1682 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001683
Ed Tanous002d39b2022-05-31 08:59:27 -07001684 std::string entryID = param;
1685 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001686
Ed Tanous002d39b2022-05-31 08:59:27 -07001687 crow::connections::systemBus->async_method_call(
1688 [asyncResp, entryID](const boost::system::error_code ec,
1689 const sdbusplus::message::unix_fd& unixfd) {
1690 if (ec.value() == EBADR)
1691 {
1692 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1693 entryID);
1694 return;
1695 }
1696 if (ec)
1697 {
1698 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1699 messages::internalError(asyncResp->res);
1700 return;
1701 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001702
Ed Tanous002d39b2022-05-31 08:59:27 -07001703 int fd = -1;
1704 fd = dup(unixfd);
1705 if (fd == -1)
1706 {
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 long long int size = lseek(fd, 0, SEEK_END);
1712 if (size == -1)
1713 {
1714 messages::internalError(asyncResp->res);
1715 return;
1716 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001717
Ed Tanous002d39b2022-05-31 08:59:27 -07001718 // Arbitrary max size of 64kb
1719 constexpr int maxFileSize = 65536;
1720 if (size > maxFileSize)
1721 {
1722 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1723 << maxFileSize;
1724 messages::internalError(asyncResp->res);
1725 return;
1726 }
1727 std::vector<char> data(static_cast<size_t>(size));
1728 long long int rc = lseek(fd, 0, SEEK_SET);
1729 if (rc == -1)
1730 {
1731 messages::internalError(asyncResp->res);
1732 return;
1733 }
1734 rc = read(fd, data.data(), data.size());
1735 if ((rc == -1) || (rc != size))
1736 {
1737 messages::internalError(asyncResp->res);
1738 return;
1739 }
1740 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001741
Ed Tanous002d39b2022-05-31 08:59:27 -07001742 std::string_view strData(data.data(), data.size());
1743 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001744
Ed Tanousd9f6c622022-03-17 09:12:17 -07001745 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07001746 "application/octet-stream");
Ed Tanousd9f6c622022-03-17 09:12:17 -07001747 asyncResp->res.addHeader(
1748 boost::beast::http::field::content_transfer_encoding, "Base64");
Ed Tanous002d39b2022-05-31 08:59:27 -07001749 asyncResp->res.body() = std::move(output);
1750 },
1751 "xyz.openbmc_project.Logging",
1752 "/xyz/openbmc_project/logging/entry/" + entryID,
1753 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1754 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001755}
1756
Spencer Kub7028eb2021-10-26 15:27:35 +08001757constexpr const char* hostLoggerFolderPath = "/var/log/console";
1758
1759inline bool
1760 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1761 std::vector<std::filesystem::path>& hostLoggerFiles)
1762{
1763 std::error_code ec;
1764 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1765 if (ec)
1766 {
1767 BMCWEB_LOG_ERROR << ec.message();
1768 return false;
1769 }
1770 for (const std::filesystem::directory_entry& it : logPath)
1771 {
1772 std::string filename = it.path().filename();
1773 // Prefix of each log files is "log". Find the file and save the
1774 // path
Ed Tanous11ba3972022-07-11 09:50:41 -07001775 if (filename.starts_with("log"))
Spencer Kub7028eb2021-10-26 15:27:35 +08001776 {
1777 hostLoggerFiles.emplace_back(it.path());
1778 }
1779 }
1780 // As the log files rotate, they are appended with a ".#" that is higher for
1781 // the older logs. Since we start from oldest logs, sort the name in
1782 // descending order.
1783 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1784 AlphanumLess<std::string>());
1785
1786 return true;
1787}
1788
Ed Tanous02cad962022-06-30 16:50:15 -07001789inline bool getHostLoggerEntries(
1790 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1791 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001792{
1793 GzFileReader logFile;
1794
1795 // Go though all log files and expose host logs.
1796 for (const std::filesystem::path& it : hostLoggerFiles)
1797 {
1798 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1799 {
1800 BMCWEB_LOG_ERROR << "fail to expose host logs";
1801 return false;
1802 }
1803 }
1804 // Get lastMessage from constructor by getter
1805 std::string lastMessage = logFile.getLastMessage();
1806 if (!lastMessage.empty())
1807 {
1808 logCount++;
1809 if (logCount > skip && logCount <= (skip + top))
1810 {
1811 logEntries.push_back(lastMessage);
1812 }
1813 }
1814 return true;
1815}
1816
1817inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1818 const std::string& msg,
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001819 nlohmann::json::object_t& logEntryJson)
Spencer Kub7028eb2021-10-26 15:27:35 +08001820{
1821 // Fill in the log entry with the gathered data.
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001822 logEntryJson["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
1823 logEntryJson["@odata.id"] =
1824 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1825 logEntryID;
1826 logEntryJson["Name"] = "Host Logger Entry";
1827 logEntryJson["Id"] = logEntryID;
1828 logEntryJson["Message"] = msg;
1829 logEntryJson["EntryType"] = "Oem";
1830 logEntryJson["Severity"] = "OK";
1831 logEntryJson["OemRecordFormat"] = "Host Logger Entry";
Spencer Kub7028eb2021-10-26 15:27:35 +08001832}
1833
1834inline void requestRoutesSystemHostLogger(App& app)
1835{
1836 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1837 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001838 .methods(boost::beast::http::verb::get)(
1839 [&app](const crow::Request& req,
1840 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001841 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001842 {
1843 return;
1844 }
1845 asyncResp->res.jsonValue["@odata.id"] =
1846 "/redfish/v1/Systems/system/LogServices/HostLogger";
1847 asyncResp->res.jsonValue["@odata.type"] =
1848 "#LogService.v1_1_0.LogService";
1849 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1850 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1851 asyncResp->res.jsonValue["Id"] = "HostLogger";
1852 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1853 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1854 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001855}
1856
1857inline void requestRoutesSystemHostLoggerCollection(App& app)
1858{
1859 BMCWEB_ROUTE(app,
1860 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1861 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001862 .methods(boost::beast::http::verb::get)(
1863 [&app](const crow::Request& req,
1864 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1865 query_param::QueryCapabilities capabilities = {
1866 .canDelegateTop = true,
1867 .canDelegateSkip = true,
1868 };
1869 query_param::Query delegatedQuery;
1870 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001871 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001872 {
1873 return;
1874 }
1875 asyncResp->res.jsonValue["@odata.id"] =
1876 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1877 asyncResp->res.jsonValue["@odata.type"] =
1878 "#LogEntryCollection.LogEntryCollection";
1879 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1880 asyncResp->res.jsonValue["Description"] =
1881 "Collection of HostLogger Entries";
1882 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1883 logEntryArray = nlohmann::json::array();
1884 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001885
Ed Tanous002d39b2022-05-31 08:59:27 -07001886 std::vector<std::filesystem::path> hostLoggerFiles;
1887 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1888 {
1889 BMCWEB_LOG_ERROR << "fail to get host log file path";
1890 return;
1891 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001892 // If we weren't provided top and skip limits, use the defaults.
1893 size_t skip = delegatedQuery.skip.value_or(0);
Jiaqing Zhao5143f7a2022-07-22 09:33:33 +08001894 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
Ed Tanous002d39b2022-05-31 08:59:27 -07001895 size_t logCount = 0;
1896 // This vector only store the entries we want to expose that
1897 // control by skip and top.
1898 std::vector<std::string> logEntries;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001899 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1900 logCount))
Ed Tanous002d39b2022-05-31 08:59:27 -07001901 {
1902 messages::internalError(asyncResp->res);
1903 return;
1904 }
1905 // If vector is empty, that means skip value larger than total
1906 // log count
1907 if (logEntries.empty())
1908 {
1909 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1910 return;
1911 }
1912 if (!logEntries.empty())
1913 {
1914 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001915 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001916 nlohmann::json::object_t hostLogEntry;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001917 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
1918 hostLogEntry);
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001919 logEntryArray.push_back(std::move(hostLogEntry));
George Liu0fda0f12021-11-16 10:06:17 +08001920 }
1921
Ed Tanous002d39b2022-05-31 08:59:27 -07001922 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001923 if (skip + top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001924 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001925 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1926 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001927 std::to_string(skip + top);
George Liu0fda0f12021-11-16 10:06:17 +08001928 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001929 }
George Liu0fda0f12021-11-16 10:06:17 +08001930 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001931}
1932
1933inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1934{
1935 BMCWEB_ROUTE(
1936 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1937 .privileges(redfish::privileges::getLogEntry)
1938 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001939 [&app](const crow::Request& req,
1940 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1941 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001942 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001943 {
1944 return;
1945 }
1946 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001947
Ed Tanous002d39b2022-05-31 08:59:27 -07001948 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001949
Ed Tanous002d39b2022-05-31 08:59:27 -07001950 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1951 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001952
Ed Tanous002d39b2022-05-31 08:59:27 -07001953 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1954 if (ec == std::errc::invalid_argument)
1955 {
1956 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1957 return;
1958 }
1959 if (ec == std::errc::result_out_of_range)
1960 {
1961 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1962 return;
1963 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001964
Ed Tanous002d39b2022-05-31 08:59:27 -07001965 std::vector<std::filesystem::path> hostLoggerFiles;
1966 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1967 {
1968 BMCWEB_LOG_ERROR << "fail to get host log file path";
1969 return;
1970 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001971
Ed Tanous002d39b2022-05-31 08:59:27 -07001972 size_t logCount = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001973 size_t top = 1;
Ed Tanous002d39b2022-05-31 08:59:27 -07001974 std::vector<std::string> logEntries;
1975 // We can get specific entry by skip and top. For example, if we
1976 // want to get nth entry, we can set skip = n-1 and top = 1 to
1977 // get that entry
1978 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1979 logCount))
1980 {
1981 messages::internalError(asyncResp->res);
1982 return;
1983 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001984
Ed Tanous002d39b2022-05-31 08:59:27 -07001985 if (!logEntries.empty())
1986 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001987 nlohmann::json::object_t hostLogEntry;
1988 fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
1989 asyncResp->res.jsonValue.update(hostLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07001990 return;
1991 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001992
Ed Tanous002d39b2022-05-31 08:59:27 -07001993 // Requested ID was not found
1994 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1995 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001996}
1997
Claire Weinanfdd26902022-03-01 14:18:25 -08001998constexpr char const* dumpManagerIface =
1999 "xyz.openbmc_project.Collection.DeleteAll";
Claire Weinandd72e872022-08-15 14:20:06 -07002000inline void handleBMCLogServicesCollectionGet(
Claire Weinanfdd26902022-03-01 14:18:25 -08002001 crow::App& app, const crow::Request& req,
2002 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2003{
2004 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2005 {
2006 return;
2007 }
2008 // Collections don't include the static data added by SubRoute
2009 // because it has a duplicate entry for members
2010 asyncResp->res.jsonValue["@odata.type"] =
2011 "#LogServiceCollection.LogServiceCollection";
2012 asyncResp->res.jsonValue["@odata.id"] =
2013 "/redfish/v1/Managers/bmc/LogServices";
2014 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2015 asyncResp->res.jsonValue["Description"] =
2016 "Collection of LogServices for this Manager";
2017 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2018 logServiceArray = nlohmann::json::array();
2019
2020#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
Ed Tanous613dabe2022-07-09 11:17:36 -07002021 nlohmann::json::object_t journal;
2022 journal["@odata.id"] = "/redfish/v1/Managers/bmc/LogServices/Journal";
2023 logServiceArray.push_back(std::move(journal));
Claire Weinanfdd26902022-03-01 14:18:25 -08002024#endif
2025
2026 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2027
2028#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2029 auto respHandler =
2030 [asyncResp](
2031 const boost::system::error_code ec,
2032 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2033 if (ec)
2034 {
2035 BMCWEB_LOG_ERROR
Claire Weinandd72e872022-08-15 14:20:06 -07002036 << "handleBMCLogServicesCollectionGet respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -08002037 << ec;
2038 // Assume that getting an error simply means there are no dump
2039 // LogServices. Return without adding any error response.
2040 return;
2041 }
2042
2043 nlohmann::json& logServiceArrayLocal =
2044 asyncResp->res.jsonValue["Members"];
2045
2046 for (const std::string& path : subTreePaths)
2047 {
2048 if (path == "/xyz/openbmc_project/dump/bmc")
2049 {
Ed Tanous613dabe2022-07-09 11:17:36 -07002050 nlohmann::json::object_t member;
2051 member["@odata.id"] =
2052 "/redfish/v1/Managers/bmc/LogServices/Dump";
2053 logServiceArrayLocal.push_back(std::move(member));
Claire Weinanfdd26902022-03-01 14:18:25 -08002054 }
2055 else if (path == "/xyz/openbmc_project/dump/faultlog")
2056 {
Ed Tanous613dabe2022-07-09 11:17:36 -07002057 nlohmann::json::object_t member;
2058 member["@odata.id"] =
2059 "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2060 logServiceArrayLocal.push_back(std::move(member));
Claire Weinanfdd26902022-03-01 14:18:25 -08002061 }
2062 }
2063
2064 asyncResp->res.jsonValue["Members@odata.count"] =
2065 logServiceArrayLocal.size();
2066 };
2067
2068 crow::connections::systemBus->async_method_call(
2069 respHandler, "xyz.openbmc_project.ObjectMapper",
2070 "/xyz/openbmc_project/object_mapper",
2071 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2072 "/xyz/openbmc_project/dump", 0,
2073 std::array<const char*, 1>{dumpManagerIface});
2074#endif
2075}
2076
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002077inline void requestRoutesBMCLogServiceCollection(App& app)
2078{
2079 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002080 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002081 .methods(boost::beast::http::verb::get)(
Claire Weinandd72e872022-08-15 14:20:06 -07002082 std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002083}
Ed Tanous1da66f72018-07-27 16:13:37 -07002084
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002085inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002086{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002087 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002088 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002089 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002090 [&app](const crow::Request& req,
2091 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002092 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002093 {
2094 return;
2095 }
2096 asyncResp->res.jsonValue["@odata.type"] =
2097 "#LogService.v1_1_0.LogService";
2098 asyncResp->res.jsonValue["@odata.id"] =
2099 "/redfish/v1/Managers/bmc/LogServices/Journal";
2100 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2101 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2102 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2103 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302104
Ed Tanous002d39b2022-05-31 08:59:27 -07002105 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002106 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002107 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2108 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2109 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302110
Ed Tanous002d39b2022-05-31 08:59:27 -07002111 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2112 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2113 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002114}
Jason M. Billse1f26342018-07-18 12:12:00 -07002115
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002116static int
2117 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2118 sd_journal* journal,
2119 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002120{
2121 // Get the Log Entry contents
2122 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002123
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002124 std::string message;
2125 std::string_view syslogID;
2126 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2127 if (ret < 0)
2128 {
2129 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2130 << strerror(-ret);
2131 }
2132 if (!syslogID.empty())
2133 {
2134 message += std::string(syslogID) + ": ";
2135 }
2136
Ed Tanous39e77502019-03-04 17:35:53 -08002137 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002138 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002139 if (ret < 0)
2140 {
2141 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2142 return 1;
2143 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002144 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002145
2146 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002147 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002148 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002149 if (ret < 0)
2150 {
2151 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002152 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002153
2154 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002155 std::string entryTimeStr;
2156 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002157 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002158 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002159 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002160
2161 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07002162 bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
2163 bmcJournalLogEntryJson["@odata.id"] =
2164 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2165 bmcJournalLogEntryID;
2166 bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2167 bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2168 bmcJournalLogEntryJson["Message"] = std::move(message);
2169 bmcJournalLogEntryJson["EntryType"] = "Oem";
2170 bmcJournalLogEntryJson["Severity"] = severity <= 2 ? "Critical"
2171 : severity <= 4 ? "Warning"
2172 : "OK";
2173 bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2174 bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
Jason M. Billse1f26342018-07-18 12:12:00 -07002175 return 0;
2176}
2177
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002178inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002179{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002180 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002181 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002182 .methods(boost::beast::http::verb::get)(
2183 [&app](const crow::Request& req,
2184 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2185 query_param::QueryCapabilities capabilities = {
2186 .canDelegateTop = true,
2187 .canDelegateSkip = true,
2188 };
2189 query_param::Query delegatedQuery;
2190 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002191 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002192 {
2193 return;
2194 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07002195
2196 size_t skip = delegatedQuery.skip.value_or(0);
Jiaqing Zhao5143f7a2022-07-22 09:33:33 +08002197 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
Ed Tanous3648c8b2022-07-25 13:39:59 -07002198
Ed Tanous002d39b2022-05-31 08:59:27 -07002199 // Collections don't include the static data added by SubRoute
2200 // because it has a duplicate entry for members
2201 asyncResp->res.jsonValue["@odata.type"] =
2202 "#LogEntryCollection.LogEntryCollection";
2203 asyncResp->res.jsonValue["@odata.id"] =
2204 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2205 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2206 asyncResp->res.jsonValue["Description"] =
2207 "Collection of BMC Journal Entries";
2208 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2209 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002210
Ed Tanous002d39b2022-05-31 08:59:27 -07002211 // Go through the journal and use the timestamp to create a
2212 // unique ID for each entry
2213 sd_journal* journalTmp = nullptr;
2214 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2215 if (ret < 0)
2216 {
2217 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2218 messages::internalError(asyncResp->res);
2219 return;
2220 }
2221 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2222 journalTmp, sd_journal_close);
2223 journalTmp = nullptr;
2224 uint64_t entryCount = 0;
2225 // Reset the unique ID on the first entry
2226 bool firstEntry = true;
2227 SD_JOURNAL_FOREACH(journal.get())
2228 {
2229 entryCount++;
2230 // Handle paging using skip (number of entries to skip from
2231 // the start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07002232 if (entryCount <= skip || entryCount > skip + top)
George Liu0fda0f12021-11-16 10:06:17 +08002233 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002234 continue;
2235 }
2236
2237 std::string idStr;
2238 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2239 {
2240 continue;
2241 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002242 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002243
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002244 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002245 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2246 bmcJournalLogEntry) != 0)
2247 {
George Liu0fda0f12021-11-16 10:06:17 +08002248 messages::internalError(asyncResp->res);
2249 return;
2250 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002251 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002252 }
2253 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07002254 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07002255 {
2256 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2257 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07002258 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07002259 }
George Liu0fda0f12021-11-16 10:06:17 +08002260 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002261}
Jason M. Billse1f26342018-07-18 12:12:00 -07002262
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002263inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002264{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002265 BMCWEB_ROUTE(app,
2266 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002267 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002268 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002269 [&app](const crow::Request& req,
2270 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2271 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002272 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002273 {
2274 return;
2275 }
2276 // Convert the unique ID back to a timestamp to find the entry
2277 uint64_t ts = 0;
2278 uint64_t index = 0;
2279 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2280 {
2281 return;
2282 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002283
Ed Tanous002d39b2022-05-31 08:59:27 -07002284 sd_journal* journalTmp = nullptr;
2285 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2286 if (ret < 0)
2287 {
2288 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2289 messages::internalError(asyncResp->res);
2290 return;
2291 }
2292 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2293 journalTmp, sd_journal_close);
2294 journalTmp = nullptr;
2295 // Go to the timestamp in the log and move to the entry at the
2296 // index tracking the unique ID
2297 std::string idStr;
2298 bool firstEntry = true;
2299 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2300 if (ret < 0)
2301 {
2302 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2303 << strerror(-ret);
2304 messages::internalError(asyncResp->res);
2305 return;
2306 }
2307 for (uint64_t i = 0; i <= index; i++)
2308 {
2309 sd_journal_next(journal.get());
2310 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2311 {
2312 messages::internalError(asyncResp->res);
2313 return;
2314 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002315 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002316 }
2317 // Confirm that the entry ID matches what was requested
2318 if (idStr != entryID)
2319 {
2320 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2321 return;
2322 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002323
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002324 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002325 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002326 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002327 {
2328 messages::internalError(asyncResp->res);
2329 return;
2330 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07002331 asyncResp->res.jsonValue.update(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002332 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002333}
2334
Claire Weinanfdd26902022-03-01 14:18:25 -08002335inline void
2336 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2337 const std::string& dumpType)
2338{
2339 std::string dumpPath;
2340 std::string overWritePolicy;
2341 bool collectDiagnosticDataSupported = false;
2342
2343 if (dumpType == "BMC")
2344 {
2345 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2346 overWritePolicy = "WrapsWhenFull";
2347 collectDiagnosticDataSupported = true;
2348 }
2349 else if (dumpType == "FaultLog")
2350 {
2351 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2352 overWritePolicy = "Unknown";
2353 collectDiagnosticDataSupported = false;
2354 }
2355 else if (dumpType == "System")
2356 {
2357 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2358 overWritePolicy = "WrapsWhenFull";
2359 collectDiagnosticDataSupported = true;
2360 }
2361 else
2362 {
2363 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2364 << dumpType;
2365 messages::internalError(asyncResp->res);
2366 return;
2367 }
2368
2369 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2370 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2371 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2372 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2373 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2374 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2375
2376 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002377 redfish::time_utils::getDateTimeOffsetNow();
Claire Weinanfdd26902022-03-01 14:18:25 -08002378 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2379 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2380 redfishDateTimeOffset.second;
2381
2382 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2383 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2384 dumpPath + "/Actions/LogService.ClearLog";
2385
2386 if (collectDiagnosticDataSupported)
2387 {
2388 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2389 ["target"] =
2390 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2391 }
2392}
2393
2394inline void handleLogServicesDumpServiceGet(
2395 crow::App& app, const std::string& dumpType, const crow::Request& req,
2396 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2397{
2398 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2399 {
2400 return;
2401 }
2402 getDumpServiceInfo(asyncResp, dumpType);
2403}
2404
2405inline void handleLogServicesDumpEntriesCollectionGet(
2406 crow::App& app, const std::string& dumpType, const crow::Request& req,
2407 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2408{
2409 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2410 {
2411 return;
2412 }
2413 getDumpEntryCollection(asyncResp, dumpType);
2414}
2415
2416inline void handleLogServicesDumpEntryGet(
2417 crow::App& app, const std::string& dumpType, const crow::Request& req,
2418 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2419 const std::string& dumpId)
2420{
2421 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2422 {
2423 return;
2424 }
2425 getDumpEntryById(asyncResp, dumpId, dumpType);
2426}
2427
2428inline void handleLogServicesDumpEntryDelete(
2429 crow::App& app, const std::string& dumpType, const crow::Request& req,
2430 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2431 const std::string& dumpId)
2432{
2433 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2434 {
2435 return;
2436 }
2437 deleteDumpEntry(asyncResp, dumpId, dumpType);
2438}
2439
2440inline void handleLogServicesDumpCollectDiagnosticDataPost(
2441 crow::App& app, const std::string& dumpType, const crow::Request& req,
2442 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2443{
2444 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2445 {
2446 return;
2447 }
2448 createDump(asyncResp, req, dumpType);
2449}
2450
2451inline void handleLogServicesDumpClearLogPost(
2452 crow::App& app, const std::string& dumpType, const crow::Request& req,
2453 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2454{
2455 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2456 {
2457 return;
2458 }
2459 clearDump(asyncResp, dumpType);
2460}
2461
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002462inline void requestRoutesBMCDumpService(App& app)
2463{
2464 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002465 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002466 .methods(boost::beast::http::verb::get)(std::bind_front(
2467 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002468}
2469
2470inline void requestRoutesBMCDumpEntryCollection(App& app)
2471{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002472 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002473 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002474 .methods(boost::beast::http::verb::get)(std::bind_front(
2475 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002476}
2477
2478inline void requestRoutesBMCDumpEntry(App& app)
2479{
2480 BMCWEB_ROUTE(app,
2481 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002482 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002483 .methods(boost::beast::http::verb::get)(std::bind_front(
2484 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2485
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002486 BMCWEB_ROUTE(app,
2487 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002488 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002489 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2490 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002491}
2492
2493inline void requestRoutesBMCDumpCreate(App& app)
2494{
George Liu0fda0f12021-11-16 10:06:17 +08002495 BMCWEB_ROUTE(
2496 app,
2497 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002498 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002499 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002500 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2501 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002502}
2503
2504inline void requestRoutesBMCDumpClear(App& app)
2505{
George Liu0fda0f12021-11-16 10:06:17 +08002506 BMCWEB_ROUTE(
2507 app,
2508 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002509 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002510 .methods(boost::beast::http::verb::post)(std::bind_front(
2511 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2512}
2513
2514inline void requestRoutesFaultLogDumpService(App& app)
2515{
2516 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2517 .privileges(redfish::privileges::getLogService)
2518 .methods(boost::beast::http::verb::get)(std::bind_front(
2519 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2520}
2521
2522inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2523{
2524 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2525 .privileges(redfish::privileges::getLogEntryCollection)
2526 .methods(boost::beast::http::verb::get)(
2527 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2528 std::ref(app), "FaultLog"));
2529}
2530
2531inline void requestRoutesFaultLogDumpEntry(App& app)
2532{
2533 BMCWEB_ROUTE(app,
2534 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2535 .privileges(redfish::privileges::getLogEntry)
2536 .methods(boost::beast::http::verb::get)(std::bind_front(
2537 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2538
2539 BMCWEB_ROUTE(app,
2540 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2541 .privileges(redfish::privileges::deleteLogEntry)
2542 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2543 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2544}
2545
2546inline void requestRoutesFaultLogDumpClear(App& app)
2547{
2548 BMCWEB_ROUTE(
2549 app,
2550 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2551 .privileges(redfish::privileges::postLogService)
2552 .methods(boost::beast::http::verb::post)(std::bind_front(
2553 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002554}
2555
2556inline void requestRoutesSystemDumpService(App& app)
2557{
2558 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002559 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002560 .methods(boost::beast::http::verb::get)(
2561 [&app](const crow::Request& req,
2562 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002563 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002564 {
2565 return;
2566 }
2567 asyncResp->res.jsonValue["@odata.id"] =
2568 "/redfish/v1/Systems/system/LogServices/Dump";
2569 asyncResp->res.jsonValue["@odata.type"] =
2570 "#LogService.v1_2_0.LogService";
2571 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2572 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2573 asyncResp->res.jsonValue["Id"] = "Dump";
2574 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302575
Ed Tanous002d39b2022-05-31 08:59:27 -07002576 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002577 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002578 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2579 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2580 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302581
Ed Tanous002d39b2022-05-31 08:59:27 -07002582 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2583 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2584 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2585 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002586
Ed Tanous002d39b2022-05-31 08:59:27 -07002587 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2588 ["target"] =
2589 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002590 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002591}
2592
2593inline void requestRoutesSystemDumpEntryCollection(App& app)
2594{
2595
2596 /**
2597 * Functions triggers appropriate requests on DBus
2598 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002599 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002600 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002601 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002602 [&app](const crow::Request& req,
2603 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002604 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002605 {
2606 return;
2607 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002608 getDumpEntryCollection(asyncResp, "System");
2609 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002610}
2611
2612inline void requestRoutesSystemDumpEntry(App& app)
2613{
2614 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002615 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002616 .privileges(redfish::privileges::getLogEntry)
2617
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002618 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002619 [&app](const crow::Request& req,
2620 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2621 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002622 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002623 {
2624 return;
2625 }
2626 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002627 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002628
2629 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002630 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002631 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002632 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002633 [&app](const crow::Request& req,
2634 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2635 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002636 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002637 {
2638 return;
2639 }
2640 deleteDumpEntry(asyncResp, param, "system");
2641 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002642}
2643
2644inline void requestRoutesSystemDumpCreate(App& app)
2645{
George Liu0fda0f12021-11-16 10:06:17 +08002646 BMCWEB_ROUTE(
2647 app,
2648 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002649 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002650 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002651 [&app](const crow::Request& req,
2652 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002653 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002654 {
2655 return;
2656 }
2657 createDump(asyncResp, req, "System");
2658 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002659}
2660
2661inline void requestRoutesSystemDumpClear(App& app)
2662{
George Liu0fda0f12021-11-16 10:06:17 +08002663 BMCWEB_ROUTE(
2664 app,
2665 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002666 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002667 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002668 [&app](const crow::Request& req,
2669 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002670
Ed Tanous45ca1b82022-03-25 13:07:27 -07002671 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002672 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002673 {
2674 return;
2675 }
2676 clearDump(asyncResp, "System");
2677 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002678}
2679
2680inline void requestRoutesCrashdumpService(App& app)
2681{
2682 // Note: Deviated from redfish privilege registry for GET & HEAD
2683 // method for security reasons.
2684 /**
2685 * Functions triggers appropriate requests on DBus
2686 */
2687 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002688 // This is incorrect, should be:
2689 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002690 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002691 .methods(boost::beast::http::verb::get)(
2692 [&app](const crow::Request& req,
2693 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002694 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002695 {
2696 return;
2697 }
2698 // Copy over the static data to include the entries added by
2699 // SubRoute
2700 asyncResp->res.jsonValue["@odata.id"] =
2701 "/redfish/v1/Systems/system/LogServices/Crashdump";
2702 asyncResp->res.jsonValue["@odata.type"] =
2703 "#LogService.v1_2_0.LogService";
2704 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2705 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2706 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2707 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2708 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302709
Ed Tanous002d39b2022-05-31 08:59:27 -07002710 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07002711 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07002712 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2713 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2714 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302715
Ed Tanous002d39b2022-05-31 08:59:27 -07002716 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2717 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2718 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2719 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2720 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2721 ["target"] =
2722 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002723 });
2724}
2725
2726void inline requestRoutesCrashdumpClear(App& app)
2727{
George Liu0fda0f12021-11-16 10:06:17 +08002728 BMCWEB_ROUTE(
2729 app,
2730 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002731 // This is incorrect, should be:
2732 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002733 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002734 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002735 [&app](const crow::Request& req,
2736 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002737 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002738 {
2739 return;
2740 }
2741 crow::connections::systemBus->async_method_call(
2742 [asyncResp](const boost::system::error_code ec,
2743 const std::string&) {
2744 if (ec)
2745 {
2746 messages::internalError(asyncResp->res);
2747 return;
2748 }
2749 messages::success(asyncResp->res);
2750 },
2751 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2752 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002753}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002754
zhanghch058d1b46d2021-04-01 11:18:24 +08002755static void
2756 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2757 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002758{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002759 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002760 [asyncResp, logID,
2761 &logEntryJson](const boost::system::error_code ec,
2762 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002763 if (ec)
2764 {
2765 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2766 if (ec.value() ==
2767 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002768 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002769 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002770 }
2771 else
2772 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002773 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002774 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002775 return;
2776 }
2777
2778 std::string timestamp{};
2779 std::string filename{};
2780 std::string logfile{};
2781 parseCrashdumpParameters(params, filename, timestamp, logfile);
2782
2783 if (filename.empty() || timestamp.empty())
2784 {
2785 messages::resourceMissingAtURI(asyncResp->res,
2786 crow::utility::urlFromPieces(logID));
2787 return;
2788 }
2789
2790 std::string crashdumpURI =
2791 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2792 logID + "/" + filename;
Jason M. Bills84afc482022-06-24 12:38:23 -07002793 nlohmann::json::object_t logEntry;
2794 logEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
2795 logEntry["@odata.id"] =
2796 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
2797 logEntry["Name"] = "CPU Crashdump";
2798 logEntry["Id"] = logID;
2799 logEntry["EntryType"] = "Oem";
2800 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2801 logEntry["DiagnosticDataType"] = "OEM";
2802 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2803 logEntry["Created"] = std::move(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07002804
2805 // If logEntryJson references an array of LogEntry resources
2806 // ('Members' list), then push this as a new entry, otherwise set it
2807 // directly
2808 if (logEntryJson.is_array())
2809 {
2810 logEntryJson.push_back(logEntry);
2811 asyncResp->res.jsonValue["Members@odata.count"] =
2812 logEntryJson.size();
2813 }
2814 else
2815 {
Jason M. Billsd405bb52022-06-24 10:52:05 -07002816 logEntryJson.update(logEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002817 }
2818 };
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02002819 sdbusplus::asio::getAllProperties(
2820 *crow::connections::systemBus, crashdumpObject,
2821 crashdumpPath + std::string("/") + logID, crashdumpInterface,
2822 std::move(getStoredLogCallback));
Jason M. Billse855dd22019-10-08 11:37:48 -07002823}
2824
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002825inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002826{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002827 // Note: Deviated from redfish privilege registry for GET & HEAD
2828 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002829 /**
2830 * Functions triggers appropriate requests on DBus
2831 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002832 BMCWEB_ROUTE(app,
2833 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002834 // This is incorrect, should be.
2835 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002836 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002837 .methods(boost::beast::http::verb::get)(
2838 [&app](const crow::Request& req,
2839 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002840 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002841 {
2842 return;
2843 }
2844 crow::connections::systemBus->async_method_call(
2845 [asyncResp](const boost::system::error_code ec,
2846 const std::vector<std::string>& resp) {
2847 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002848 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002849 if (ec.value() !=
2850 boost::system::errc::no_such_file_or_directory)
2851 {
2852 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2853 << ec.message();
2854 messages::internalError(asyncResp->res);
2855 return;
2856 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002857 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002858 asyncResp->res.jsonValue["@odata.type"] =
2859 "#LogEntryCollection.LogEntryCollection";
2860 asyncResp->res.jsonValue["@odata.id"] =
2861 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2862 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2863 asyncResp->res.jsonValue["Description"] =
2864 "Collection of Crashdump Entries";
2865 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2866 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002867
Ed Tanous002d39b2022-05-31 08:59:27 -07002868 for (const std::string& path : resp)
2869 {
2870 const sdbusplus::message::object_path objPath(path);
2871 // Get the log ID
2872 std::string logID = objPath.filename();
2873 if (logID.empty())
2874 {
2875 continue;
2876 }
2877 // Add the log entry to the array
2878 logCrashdumpEntry(asyncResp, logID,
2879 asyncResp->res.jsonValue["Members"]);
2880 }
2881 },
2882 "xyz.openbmc_project.ObjectMapper",
2883 "/xyz/openbmc_project/object_mapper",
2884 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2885 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002886 });
2887}
Ed Tanous1da66f72018-07-27 16:13:37 -07002888
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002889inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002890{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002891 // Note: Deviated from redfish privilege registry for GET & HEAD
2892 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002893
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002894 BMCWEB_ROUTE(
2895 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002896 // this is incorrect, should be
2897 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002898 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002899 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002900 [&app](const crow::Request& req,
2901 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2902 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002903 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002904 {
2905 return;
2906 }
2907 const std::string& logID = param;
2908 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2909 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002910}
Ed Tanous1da66f72018-07-27 16:13:37 -07002911
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002912inline void requestRoutesCrashdumpFile(App& app)
2913{
2914 // Note: Deviated from redfish privilege registry for GET & HEAD
2915 // method for security reasons.
2916 BMCWEB_ROUTE(
2917 app,
2918 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002919 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002920 .methods(boost::beast::http::verb::get)(
Nan Zhoua4ce1142022-08-02 18:45:25 +00002921 [](const crow::Request& req,
2922 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2923 const std::string& logID, const std::string& fileName) {
Shounak Mitra2a9beee2022-07-20 18:41:30 +00002924 // Do not call getRedfishRoute here since the crashdump file is not a
2925 // Redfish resource.
Ed Tanous002d39b2022-05-31 08:59:27 -07002926 auto getStoredLogCallback =
2927 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2928 const boost::system::error_code ec,
2929 const std::vector<
2930 std::pair<std::string, dbus::utility::DbusVariantType>>&
2931 resp) {
2932 if (ec)
2933 {
2934 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2935 messages::internalError(asyncResp->res);
2936 return;
2937 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002938
Ed Tanous002d39b2022-05-31 08:59:27 -07002939 std::string dbusFilename{};
2940 std::string dbusTimestamp{};
2941 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002942
Ed Tanous002d39b2022-05-31 08:59:27 -07002943 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2944 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002945
Ed Tanous002d39b2022-05-31 08:59:27 -07002946 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2947 dbusFilepath.empty())
2948 {
2949 messages::resourceMissingAtURI(asyncResp->res, url);
2950 return;
2951 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002952
Ed Tanous002d39b2022-05-31 08:59:27 -07002953 // Verify the file name parameter is correct
2954 if (fileName != dbusFilename)
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 if (!std::filesystem::exists(dbusFilepath))
2961 {
2962 messages::resourceMissingAtURI(asyncResp->res, url);
2963 return;
2964 }
2965 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2966 asyncResp->res.body() =
2967 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002968
Ed Tanous002d39b2022-05-31 08:59:27 -07002969 // Configure this to be a file download when accessed
2970 // from a browser
Ed Tanousd9f6c622022-03-17 09:12:17 -07002971 asyncResp->res.addHeader(
2972 boost::beast::http::field::content_disposition, "attachment");
Ed Tanous002d39b2022-05-31 08:59:27 -07002973 };
Krzysztof Grobelnyd1bde9e2022-09-07 10:40:51 +02002974 sdbusplus::asio::getAllProperties(
2975 *crow::connections::systemBus, crashdumpObject,
2976 crashdumpPath + std::string("/") + logID, crashdumpInterface,
2977 std::move(getStoredLogCallback));
Ed Tanous002d39b2022-05-31 08:59:27 -07002978 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002979}
2980
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002981enum class OEMDiagnosticType
2982{
2983 onDemand,
2984 telemetry,
2985 invalid,
2986};
2987
Ed Tanousf7725d72022-03-07 12:46:00 -08002988inline OEMDiagnosticType
2989 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002990{
2991 if (oemDiagStr == "OnDemand")
2992 {
2993 return OEMDiagnosticType::onDemand;
2994 }
2995 if (oemDiagStr == "Telemetry")
2996 {
2997 return OEMDiagnosticType::telemetry;
2998 }
2999
3000 return OEMDiagnosticType::invalid;
3001}
3002
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003003inline void requestRoutesCrashdumpCollect(App& app)
3004{
3005 // Note: Deviated from redfish privilege registry for GET & HEAD
3006 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08003007 BMCWEB_ROUTE(
3008 app,
3009 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07003010 // The below is incorrect; Should be ConfigureManager
3011 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003012 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07003013 .methods(boost::beast::http::verb::post)(
3014 [&app](const crow::Request& req,
3015 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003016 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003017 {
3018 return;
3019 }
3020 std::string diagnosticDataType;
3021 std::string oemDiagnosticDataType;
3022 if (!redfish::json_util::readJsonAction(
3023 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3024 "OEMDiagnosticDataType", oemDiagnosticDataType))
3025 {
3026 return;
3027 }
3028
3029 if (diagnosticDataType != "OEM")
3030 {
3031 BMCWEB_LOG_ERROR
3032 << "Only OEM DiagnosticDataType supported for Crashdump";
3033 messages::actionParameterValueFormatError(
3034 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3035 "CollectDiagnosticData");
3036 return;
3037 }
3038
3039 OEMDiagnosticType oemDiagType =
3040 getOEMDiagnosticType(oemDiagnosticDataType);
3041
3042 std::string iface;
3043 std::string method;
3044 std::string taskMatchStr;
3045 if (oemDiagType == OEMDiagnosticType::onDemand)
3046 {
3047 iface = crashdumpOnDemandInterface;
3048 method = "GenerateOnDemandLog";
3049 taskMatchStr = "type='signal',"
3050 "interface='org.freedesktop.DBus.Properties',"
3051 "member='PropertiesChanged',"
3052 "arg0namespace='com.intel.crashdump'";
3053 }
3054 else if (oemDiagType == OEMDiagnosticType::telemetry)
3055 {
3056 iface = crashdumpTelemetryInterface;
3057 method = "GenerateTelemetryLog";
3058 taskMatchStr = "type='signal',"
3059 "interface='org.freedesktop.DBus.Properties',"
3060 "member='PropertiesChanged',"
3061 "arg0namespace='com.intel.crashdump'";
3062 }
3063 else
3064 {
3065 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3066 << oemDiagnosticDataType;
3067 messages::actionParameterValueFormatError(
3068 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3069 "CollectDiagnosticData");
3070 return;
3071 }
3072
3073 auto collectCrashdumpCallback =
3074 [asyncResp, payload(task::Payload(req)),
3075 taskMatchStr](const boost::system::error_code ec,
3076 const std::string&) mutable {
3077 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003078 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003079 if (ec.value() == boost::system::errc::operation_not_supported)
3080 {
3081 messages::resourceInStandby(asyncResp->res);
3082 }
3083 else if (ec.value() ==
3084 boost::system::errc::device_or_resource_busy)
3085 {
3086 messages::serviceTemporarilyUnavailable(asyncResp->res,
3087 "60");
3088 }
3089 else
3090 {
3091 messages::internalError(asyncResp->res);
3092 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003093 return;
3094 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003095 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -05003096 [](boost::system::error_code err, sdbusplus::message_t&,
Ed Tanous002d39b2022-05-31 08:59:27 -07003097 const std::shared_ptr<task::TaskData>& taskData) {
3098 if (!err)
3099 {
3100 taskData->messages.emplace_back(messages::taskCompletedOK(
3101 std::to_string(taskData->index)));
3102 taskData->state = "Completed";
3103 }
3104 return task::completed;
3105 },
3106 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003107
Ed Tanous002d39b2022-05-31 08:59:27 -07003108 task->startTimer(std::chrono::minutes(5));
3109 task->populateResp(asyncResp->res);
3110 task->payload.emplace(std::move(payload));
3111 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003112
Ed Tanous002d39b2022-05-31 08:59:27 -07003113 crow::connections::systemBus->async_method_call(
3114 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3115 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003116 });
3117}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003118
Andrew Geisslercb92c032018-08-17 07:56:14 -07003119/**
3120 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3121 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003122inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003123{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003124 /**
3125 * Function handles POST method request.
3126 * The Clear Log actions does not require any parameter.The action deletes
3127 * all entries found in the Entries collection for this Log Service.
3128 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003129
George Liu0fda0f12021-11-16 10:06:17 +08003130 BMCWEB_ROUTE(
3131 app,
3132 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003133 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003134 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003135 [&app](const crow::Request& req,
3136 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003137 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003138 {
3139 return;
3140 }
3141 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003142
Ed Tanous002d39b2022-05-31 08:59:27 -07003143 // Process response from Logging service.
3144 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3145 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3146 if (ec)
3147 {
3148 // TODO Handle for specific error code
3149 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3150 asyncResp->res.result(
3151 boost::beast::http::status::internal_server_error);
3152 return;
3153 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003154
Ed Tanous002d39b2022-05-31 08:59:27 -07003155 asyncResp->res.result(boost::beast::http::status::no_content);
3156 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003157
Ed Tanous002d39b2022-05-31 08:59:27 -07003158 // Make call to Logging service to request Clear Log
3159 crow::connections::systemBus->async_method_call(
3160 respHandler, "xyz.openbmc_project.Logging",
3161 "/xyz/openbmc_project/logging",
3162 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3163 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003164}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003165
3166/****************************************************
3167 * Redfish PostCode interfaces
3168 * using DBUS interface: getPostCodesTS
3169 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003170inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003171{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003172 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003173 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003174 .methods(boost::beast::http::verb::get)(
3175 [&app](const crow::Request& req,
3176 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003177 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003178 {
3179 return;
3180 }
Ed Tanous14766872022-03-15 10:44:42 -07003181
Ed Tanous002d39b2022-05-31 08:59:27 -07003182 asyncResp->res.jsonValue["@odata.id"] =
3183 "/redfish/v1/Systems/system/LogServices/PostCodes";
3184 asyncResp->res.jsonValue["@odata.type"] =
3185 "#LogService.v1_1_0.LogService";
3186 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3187 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3188 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3189 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3190 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3191 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303192
Ed Tanous002d39b2022-05-31 08:59:27 -07003193 std::pair<std::string, std::string> redfishDateTimeOffset =
Ed Tanous2b829372022-08-03 14:22:34 -07003194 redfish::time_utils::getDateTimeOffsetNow();
Ed Tanous002d39b2022-05-31 08:59:27 -07003195 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3196 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3197 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303198
Ed Tanous002d39b2022-05-31 08:59:27 -07003199 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3200 {"target",
3201 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003202 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003203}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003204
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003205inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003206{
George Liu0fda0f12021-11-16 10:06:17 +08003207 BMCWEB_ROUTE(
3208 app,
3209 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003210 // The following privilege is incorrect; It should be ConfigureManager
3211 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003212 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003213 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003214 [&app](const crow::Request& req,
3215 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003216 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003217 {
3218 return;
3219 }
3220 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003221
Ed Tanous002d39b2022-05-31 08:59:27 -07003222 // Make call to post-code service to request clear all
3223 crow::connections::systemBus->async_method_call(
3224 [asyncResp](const boost::system::error_code ec) {
3225 if (ec)
3226 {
3227 // TODO Handle for specific error code
3228 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3229 << ec;
3230 asyncResp->res.result(
3231 boost::beast::http::status::internal_server_error);
3232 messages::internalError(asyncResp->res);
3233 return;
3234 }
3235 },
3236 "xyz.openbmc_project.State.Boot.PostCode0",
3237 "/xyz/openbmc_project/State/Boot/PostCode0",
3238 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3239 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003240}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003241
3242static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003243 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303244 const boost::container::flat_map<
3245 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003246 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3247 const uint64_t skip = 0, const uint64_t top = 0)
3248{
3249 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003250 const registries::Message* message =
3251 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003252
3253 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003254 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003255
3256 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303257 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3258 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003259 {
3260 currentCodeIndex++;
3261 std::string postcodeEntryID =
3262 "B" + std::to_string(bootIndex) + "-" +
3263 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3264
3265 uint64_t usecSinceEpoch = code.first;
3266 uint64_t usTimeOffset = 0;
3267
3268 if (1 == currentCodeIndex)
3269 { // already incremented
3270 firstCodeTimeUs = code.first;
3271 }
3272 else
3273 {
3274 usTimeOffset = code.first - firstCodeTimeUs;
3275 }
3276
3277 // skip if no specific codeIndex is specified and currentCodeIndex does
3278 // not fall between top and skip
3279 if ((codeIndex == 0) &&
3280 (currentCodeIndex <= skip || currentCodeIndex > top))
3281 {
3282 continue;
3283 }
3284
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003285 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003286 // currentIndex
3287 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3288 {
3289 // This is done for simplicity. 1st entry is needed to calculate
3290 // time offset. To improve efficiency, one can get to the entry
3291 // directly (possibly with flatmap's nth method)
3292 continue;
3293 }
3294
3295 // currentCodeIndex is within top and skip or equal to specified code
3296 // index
3297
3298 // Get the Created time from the timestamp
3299 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003300 entryTimeStr =
Ed Tanous2b829372022-08-03 14:22:34 -07003301 redfish::time_utils::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003302
3303 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3304 std::ostringstream hexCode;
3305 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303306 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003307 std::ostringstream timeOffsetStr;
3308 // Set Fixed -Point Notation
3309 timeOffsetStr << std::fixed;
3310 // Set precision to 4 digits
3311 timeOffsetStr << std::setprecision(4);
3312 // Add double to stream
3313 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3314 std::vector<std::string> messageArgs = {
3315 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3316
3317 // Get MessageArgs template from message registry
3318 std::string msg;
3319 if (message != nullptr)
3320 {
3321 msg = message->message;
3322
3323 // fill in this post code value
3324 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003325 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003326 {
3327 std::string argStr = "%" + std::to_string(++i);
3328 size_t argPos = msg.find(argStr);
3329 if (argPos != std::string::npos)
3330 {
3331 msg.replace(argPos, argStr.length(), messageArg);
3332 }
3333 }
3334 }
3335
Tim Leed4342a92020-04-27 11:47:58 +08003336 // Get Severity template from message registry
3337 std::string severity;
3338 if (message != nullptr)
3339 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003340 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003341 }
3342
ZhikuiRena3316fc2020-01-29 14:58:08 -08003343 // add to AsyncResp
3344 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003345 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills84afc482022-06-24 12:38:23 -07003346 bmcLogEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
3347 bmcLogEntry["@odata.id"] =
3348 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3349 postcodeEntryID;
3350 bmcLogEntry["Name"] = "POST Code Log Entry";
3351 bmcLogEntry["Id"] = postcodeEntryID;
3352 bmcLogEntry["Message"] = std::move(msg);
3353 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3354 bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3355 bmcLogEntry["EntryType"] = "Event";
3356 bmcLogEntry["Severity"] = std::move(severity);
3357 bmcLogEntry["Created"] = entryTimeStr;
George Liu647b3cd2021-07-05 12:43:56 +08003358 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3359 {
3360 bmcLogEntry["AdditionalDataURI"] =
3361 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3362 postcodeEntryID + "/attachment";
3363 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003364 }
3365}
3366
zhanghch058d1b46d2021-04-01 11:18:24 +08003367static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003368 const uint16_t bootIndex,
3369 const uint64_t codeIndex)
3370{
3371 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303372 [aResp, bootIndex,
3373 codeIndex](const boost::system::error_code ec,
3374 const boost::container::flat_map<
3375 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3376 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003377 if (ec)
3378 {
3379 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3380 messages::internalError(aResp->res);
3381 return;
3382 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003383
Ed Tanous002d39b2022-05-31 08:59:27 -07003384 // skip the empty postcode boots
3385 if (postcode.empty())
3386 {
3387 return;
3388 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003389
Ed Tanous002d39b2022-05-31 08:59:27 -07003390 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003391
Ed Tanous002d39b2022-05-31 08:59:27 -07003392 aResp->res.jsonValue["Members@odata.count"] =
3393 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003394 },
Jonathan Doman15124762021-01-07 17:54:17 -08003395 "xyz.openbmc_project.State.Boot.PostCode0",
3396 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003397 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3398 bootIndex);
3399}
3400
zhanghch058d1b46d2021-04-01 11:18:24 +08003401static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003402 const uint16_t bootIndex,
3403 const uint16_t bootCount,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003404 const uint64_t entryCount, size_t skip,
3405 size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003406{
3407 crow::connections::systemBus->async_method_call(
3408 [aResp, bootIndex, bootCount, entryCount, skip,
3409 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303410 const boost::container::flat_map<
3411 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3412 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003413 if (ec)
3414 {
3415 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3416 messages::internalError(aResp->res);
3417 return;
3418 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003419
Ed Tanous002d39b2022-05-31 08:59:27 -07003420 uint64_t endCount = entryCount;
3421 if (!postcode.empty())
3422 {
3423 endCount = entryCount + postcode.size();
Ed Tanous3648c8b2022-07-25 13:39:59 -07003424 if (skip < endCount && (top + skip) > entryCount)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003425 {
Ed Tanous3648c8b2022-07-25 13:39:59 -07003426 uint64_t thisBootSkip =
3427 std::max(static_cast<uint64_t>(skip), entryCount) -
3428 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003429 uint64_t thisBootTop =
Ed Tanous3648c8b2022-07-25 13:39:59 -07003430 std::min(static_cast<uint64_t>(top + skip), endCount) -
3431 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003432
3433 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3434 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003435 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003436 aResp->res.jsonValue["Members@odata.count"] = endCount;
3437 }
3438
3439 // continue to previous bootIndex
3440 if (bootIndex < bootCount)
3441 {
3442 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3443 bootCount, endCount, skip, top);
3444 }
Jiaqing Zhao81584ab2022-07-28 00:33:45 +08003445 else if (skip + top < endCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07003446 {
3447 aResp->res.jsonValue["Members@odata.nextLink"] =
3448 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3449 std::to_string(skip + top);
3450 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003451 },
Jonathan Doman15124762021-01-07 17:54:17 -08003452 "xyz.openbmc_project.State.Boot.PostCode0",
3453 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003454 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3455 bootIndex);
3456}
3457
zhanghch058d1b46d2021-04-01 11:18:24 +08003458static void
3459 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003460 size_t skip, size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003461{
3462 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003463 sdbusplus::asio::getProperty<uint16_t>(
3464 *crow::connections::systemBus,
3465 "xyz.openbmc_project.State.Boot.PostCode0",
3466 "/xyz/openbmc_project/State/Boot/PostCode0",
3467 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3468 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3469 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003470 if (ec)
3471 {
3472 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3473 messages::internalError(aResp->res);
3474 return;
3475 }
3476 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003477 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003478}
3479
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003480inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003481{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003482 BMCWEB_ROUTE(app,
3483 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003484 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003485 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003486 [&app](const crow::Request& req,
3487 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003488 query_param::QueryCapabilities capabilities = {
3489 .canDelegateTop = true,
3490 .canDelegateSkip = true,
3491 };
3492 query_param::Query delegatedQuery;
3493 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003494 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003495 {
3496 return;
3497 }
3498 asyncResp->res.jsonValue["@odata.type"] =
3499 "#LogEntryCollection.LogEntryCollection";
3500 asyncResp->res.jsonValue["@odata.id"] =
3501 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3502 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3503 asyncResp->res.jsonValue["Description"] =
3504 "Collection of POST Code Log Entries";
3505 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3506 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07003507 size_t skip = delegatedQuery.skip.value_or(0);
Jiaqing Zhao5143f7a2022-07-22 09:33:33 +08003508 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
Ed Tanous3648c8b2022-07-25 13:39:59 -07003509 getCurrentBootNumber(asyncResp, skip, top);
Ed Tanous002d39b2022-05-31 08:59:27 -07003510 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003511}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003512
George Liu647b3cd2021-07-05 12:43:56 +08003513/**
3514 * @brief Parse post code ID and get the current value and index value
3515 * eg: postCodeID=B1-2, currentValue=1, index=2
3516 *
3517 * @param[in] postCodeID Post Code ID
3518 * @param[out] currentValue Current value
3519 * @param[out] index Index value
3520 *
3521 * @return bool true if the parsing is successful, false the parsing fails
3522 */
3523inline static bool parsePostCode(const std::string& postCodeID,
3524 uint64_t& currentValue, uint16_t& index)
3525{
3526 std::vector<std::string> split;
3527 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3528 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3529 {
3530 return false;
3531 }
3532
Ed Tanousca45aa32022-01-07 09:28:45 -08003533 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003534 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003535 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003536 const char* end = split[0].data() + split[0].size();
3537 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3538
3539 if (ptrIndex != end || ecIndex != std::errc())
3540 {
3541 return false;
3542 }
3543
3544 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003545
3546 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003547 end = split[1].data() + split[1].size();
3548 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003549
Tony Lee517d9a52022-06-28 15:41:23 +08003550 return ptrValue == end && ecValue == std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003551}
3552
3553inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3554{
George Liu0fda0f12021-11-16 10:06:17 +08003555 BMCWEB_ROUTE(
3556 app,
3557 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003558 .privileges(redfish::privileges::getLogEntry)
3559 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003560 [&app](const crow::Request& req,
3561 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3562 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003563 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003564 {
3565 return;
3566 }
Ed Tanous99351cd2022-08-07 16:42:51 -07003567 if (http_helpers::isContentTypeAllowed(
3568 req.getHeaderValue("Accept"),
3569 http_helpers::ContentType::OctetStream))
Ed Tanous002d39b2022-05-31 08:59:27 -07003570 {
3571 asyncResp->res.result(boost::beast::http::status::bad_request);
3572 return;
3573 }
George Liu647b3cd2021-07-05 12:43:56 +08003574
Ed Tanous002d39b2022-05-31 08:59:27 -07003575 uint64_t currentValue = 0;
3576 uint16_t index = 0;
3577 if (!parsePostCode(postCodeID, currentValue, index))
3578 {
3579 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3580 return;
3581 }
George Liu647b3cd2021-07-05 12:43:56 +08003582
Ed Tanous002d39b2022-05-31 08:59:27 -07003583 crow::connections::systemBus->async_method_call(
3584 [asyncResp, postCodeID, currentValue](
3585 const boost::system::error_code ec,
3586 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3587 postcodes) {
3588 if (ec.value() == EBADR)
3589 {
3590 messages::resourceNotFound(asyncResp->res, "LogEntry",
3591 postCodeID);
3592 return;
3593 }
3594 if (ec)
3595 {
3596 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3597 messages::internalError(asyncResp->res);
3598 return;
3599 }
George Liu647b3cd2021-07-05 12:43:56 +08003600
Ed Tanous002d39b2022-05-31 08:59:27 -07003601 size_t value = static_cast<size_t>(currentValue) - 1;
3602 if (value == std::string::npos || postcodes.size() < currentValue)
3603 {
3604 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3605 messages::resourceNotFound(asyncResp->res, "LogEntry",
3606 postCodeID);
3607 return;
3608 }
George Liu647b3cd2021-07-05 12:43:56 +08003609
Ed Tanous002d39b2022-05-31 08:59:27 -07003610 const auto& [tID, c] = postcodes[value];
3611 if (c.empty())
3612 {
3613 BMCWEB_LOG_INFO << "No found post code data";
3614 messages::resourceNotFound(asyncResp->res, "LogEntry",
3615 postCodeID);
3616 return;
3617 }
3618 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3619 const char* d = reinterpret_cast<const char*>(c.data());
3620 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003621
Ed Tanousd9f6c622022-03-17 09:12:17 -07003622 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07003623 "application/octet-stream");
Ed Tanousd9f6c622022-03-17 09:12:17 -07003624 asyncResp->res.addHeader(
3625 boost::beast::http::field::content_transfer_encoding, "Base64");
Ed Tanous002d39b2022-05-31 08:59:27 -07003626 asyncResp->res.body() = crow::utility::base64encode(strData);
3627 },
3628 "xyz.openbmc_project.State.Boot.PostCode0",
3629 "/xyz/openbmc_project/State/Boot/PostCode0",
3630 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3631 });
George Liu647b3cd2021-07-05 12:43:56 +08003632}
3633
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003634inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003635{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003636 BMCWEB_ROUTE(
3637 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003638 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003639 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003640 [&app](const crow::Request& req,
3641 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3642 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003643 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003644 {
3645 return;
3646 }
3647 uint16_t bootIndex = 0;
3648 uint64_t codeIndex = 0;
3649 if (!parsePostCode(targetID, codeIndex, bootIndex))
3650 {
3651 // Requested ID was not found
3652 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3653 return;
3654 }
3655 if (bootIndex == 0 || codeIndex == 0)
3656 {
3657 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3658 << targetID;
3659 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003660
Ed Tanous002d39b2022-05-31 08:59:27 -07003661 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3662 asyncResp->res.jsonValue["@odata.id"] =
3663 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3664 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3665 asyncResp->res.jsonValue["Description"] =
3666 "Collection of POST Code Log Entries";
3667 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3668 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003669
Ed Tanous002d39b2022-05-31 08:59:27 -07003670 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3671 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003672}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003673
Ed Tanous1da66f72018-07-27 16:13:37 -07003674} // namespace redfish