blob: e474f0ac44f073746e908b505d1d533f63e16182 [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>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060030#include <boost/algorithm/string/replace.hpp>
Jason M. Bills4851d452019-03-28 11:27:48 -070031#include <boost/algorithm/string/split.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060032#include <boost/beast/http.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070033#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080034#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080035#include <dbus_utility.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070036#include <error_messages.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070037#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050038
George Liu647b3cd2021-07-05 12:43:56 +080039#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070040#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080041#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070042#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070043#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080044#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070045
46namespace redfish
47{
48
Gunnar Mills1214b7e2020-06-04 10:11:30 -050049constexpr char const* crashdumpObject = "com.intel.crashdump";
50constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050051constexpr char const* crashdumpInterface = "com.intel.crashdump";
52constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070053 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050054constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070055 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070056constexpr char const* crashdumpTelemetryInterface =
57 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070058
Ed Tanousfffb8c12022-02-07 23:53:03 -080059namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -070060{
Ed Tanous26702d02021-11-03 15:02:33 -070061static const Message*
62 getMessageFromRegistry(const std::string& messageKey,
63 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070064{
Ed Tanous26702d02021-11-03 15:02:33 -070065 std::span<const MessageEntry>::iterator messageIt = std::find_if(
66 registry.begin(), registry.end(),
67 [&messageKey](const MessageEntry& messageEntry) {
Ed Tanouse662eae2022-01-25 10:39:19 -080068 return std::strcmp(messageEntry.first, messageKey.c_str()) == 0;
Ed Tanous26702d02021-11-03 15:02:33 -070069 });
70 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070071 {
72 return &messageIt->second;
73 }
74
75 return nullptr;
76}
77
Gunnar Mills1214b7e2020-06-04 10:11:30 -050078static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070079{
80 // Redfish MessageIds are in the form
81 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
82 // the right Message
83 std::vector<std::string> fields;
84 fields.reserve(4);
85 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050086 std::string& registryName = fields[0];
87 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070088
89 // Find the right registry and check it for the MessageKey
90 if (std::string(base::header.registryPrefix) == registryName)
91 {
92 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070093 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070094 }
95 if (std::string(openbmc::header.registryPrefix) == registryName)
96 {
97 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070098 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070099 }
100 return nullptr;
101}
Ed Tanousfffb8c12022-02-07 23:53:03 -0800102} // namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -0700103
James Feistf6150402019-01-08 10:36:20 -0800104namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700105
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500106inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700107{
Ed Tanousd4d25792020-09-29 15:15:03 -0700108 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
109 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
110 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
111 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700112 {
113 return "Critical";
114 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700115 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
116 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
117 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700118 {
119 return "OK";
120 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700121 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700122 {
123 return "Warning";
124 }
125 return "";
126}
127
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700128inline static int getJournalMetadata(sd_journal* journal,
129 const std::string_view& field,
130 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700131{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500132 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700133 size_t length = 0;
134 int ret = 0;
135 // Get the metadata from the requested field of the journal entry
Ed Tanous46ff87b2022-01-07 09:25:51 -0800136 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
137 const void** dataVoid = reinterpret_cast<const void**>(&data);
138
139 ret = sd_journal_get_data(journal, field.data(), dataVoid, &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700140 if (ret < 0)
141 {
142 return ret;
143 }
Ed Tanous39e77502019-03-04 17:35:53 -0800144 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700145 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000146 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700147 return ret;
148}
149
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700150inline static int getJournalMetadata(sd_journal* journal,
151 const std::string_view& field,
152 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700153{
154 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800155 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700156 // Get the metadata from the requested field of the journal entry
157 ret = getJournalMetadata(journal, field, metadata);
158 if (ret < 0)
159 {
160 return ret;
161 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000162 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700163 return ret;
164}
165
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700166inline static bool getEntryTimestamp(sd_journal* journal,
167 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800168{
169 int ret = 0;
170 uint64_t timestamp = 0;
171 ret = sd_journal_get_realtime_usec(journal, &timestamp);
172 if (ret < 0)
173 {
174 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
175 << strerror(-ret);
176 return false;
177 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800178 entryTimestamp = crow::utility::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500179 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800180}
Ed Tanous50b8a432022-02-03 16:29:50 -0800181
Ed Tanous67df0732021-10-26 11:23:56 -0700182static bool getSkipParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
183 const crow::Request& req, uint64_t& skip)
184{
185 boost::urls::params_view::iterator it = req.urlView.params().find("$skip");
186 if (it != req.urlView.params().end())
187 {
188 std::from_chars_result r = std::from_chars(
189 (*it).value.data(), (*it).value.data() + (*it).value.size(), skip);
190 if (r.ec != std::errc())
191 {
192 messages::queryParameterValueTypeError(asyncResp->res, "", "$skip");
193 return false;
194 }
195 }
196 return true;
197}
198
199static constexpr const uint64_t maxEntriesPerPage = 1000;
200static bool getTopParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
201 const crow::Request& req, uint64_t& top)
202{
203 boost::urls::params_view::iterator it = req.urlView.params().find("$top");
204 if (it != req.urlView.params().end())
205 {
206 std::from_chars_result r = std::from_chars(
207 (*it).value.data(), (*it).value.data() + (*it).value.size(), top);
208 if (r.ec != std::errc())
209 {
210 messages::queryParameterValueTypeError(asyncResp->res, "", "$top");
211 return false;
212 }
213 if (top < 1U || top > maxEntriesPerPage)
214 {
215
216 messages::queryParameterOutOfRange(
217 asyncResp->res, std::to_string(top), "$top",
218 "1-" + std::to_string(maxEntriesPerPage));
219 return false;
220 }
221 }
222 return true;
223}
224
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700225inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
226 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700227{
228 int ret = 0;
229 static uint64_t prevTs = 0;
230 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700231 if (firstEntry)
232 {
233 prevTs = 0;
234 }
235
Jason M. Bills16428a12018-11-02 12:42:29 -0700236 // Get the entry timestamp
237 uint64_t curTs = 0;
238 ret = sd_journal_get_realtime_usec(journal, &curTs);
239 if (ret < 0)
240 {
241 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
242 << strerror(-ret);
243 return false;
244 }
245 // If the timestamp isn't unique, increment the index
246 if (curTs == prevTs)
247 {
248 index++;
249 }
250 else
251 {
252 // Otherwise, reset it
253 index = 0;
254 }
255 // Save the timestamp
256 prevTs = curTs;
257
258 entryID = std::to_string(curTs);
259 if (index > 0)
260 {
261 entryID += "_" + std::to_string(index);
262 }
263 return true;
264}
265
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500266static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700267 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700268{
Ed Tanous271584a2019-07-09 16:24:22 -0700269 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700270 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700271 if (firstEntry)
272 {
273 prevTs = 0;
274 }
275
Jason M. Bills95820182019-04-22 16:25:34 -0700276 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700277 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700278 std::tm timeStruct = {};
279 std::istringstream entryStream(logEntry);
280 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
281 {
282 curTs = std::mktime(&timeStruct);
283 }
284 // If the timestamp isn't unique, increment the index
285 if (curTs == prevTs)
286 {
287 index++;
288 }
289 else
290 {
291 // Otherwise, reset it
292 index = 0;
293 }
294 // Save the timestamp
295 prevTs = curTs;
296
297 entryID = std::to_string(curTs);
298 if (index > 0)
299 {
300 entryID += "_" + std::to_string(index);
301 }
302 return true;
303}
304
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700305inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800306 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
307 const std::string& entryID, uint64_t& timestamp,
308 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700309{
310 if (entryID.empty())
311 {
312 return false;
313 }
314 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800315 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700316
Ed Tanous81ce6092020-12-17 16:54:55 +0000317 auto underscorePos = tsStr.find('_');
Ed Tanous71d5d8d2022-01-25 11:04:33 -0800318 if (underscorePos != std::string_view::npos)
Jason M. Bills16428a12018-11-02 12:42:29 -0700319 {
320 // Timestamp has an index
321 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800322 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700323 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700324 auto [ptr, ec] = std::from_chars(
325 indexStr.data(), indexStr.data() + indexStr.size(), index);
326 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700327 {
Ed Tanousace85d62021-10-26 12:45:59 -0700328 messages::resourceMissingAtURI(
329 asyncResp->res, crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700330 return false;
331 }
332 }
333 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700334 auto [ptr, ec] =
335 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
336 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700337 {
Ed Tanousace85d62021-10-26 12:45:59 -0700338 messages::resourceMissingAtURI(asyncResp->res,
339 crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 return false;
341 }
342 return true;
343}
344
Jason M. Bills95820182019-04-22 16:25:34 -0700345static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500346 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700347{
348 static const std::filesystem::path redfishLogDir = "/var/log";
349 static const std::string redfishLogFilename = "redfish";
350
351 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500352 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700353 std::filesystem::directory_iterator(redfishLogDir))
354 {
355 // If we find a redfish log file, save the path
356 std::string filename = dirEnt.path().filename();
357 if (boost::starts_with(filename, redfishLogFilename))
358 {
359 redfishLogFiles.emplace_back(redfishLogDir / filename);
360 }
361 }
362 // As the log files rotate, they are appended with a ".#" that is higher for
363 // the older logs. Since we don't expect more than 10 log files, we
364 // can just sort the list to get them in order from newest to oldest
365 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
366
367 return !redfishLogFiles.empty();
368}
369
zhanghch058d1b46d2021-04-01 11:18:24 +0800370inline void
371 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
372 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500373{
374 std::string dumpPath;
375 if (dumpType == "BMC")
376 {
377 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
378 }
379 else if (dumpType == "System")
380 {
381 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
382 }
383 else
384 {
385 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
386 messages::internalError(asyncResp->res);
387 return;
388 }
389
390 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800391 [asyncResp, dumpPath,
392 dumpType](const boost::system::error_code ec,
393 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500394 if (ec)
395 {
396 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
397 messages::internalError(asyncResp->res);
398 return;
399 }
400
401 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
402 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500403 std::string dumpEntryPath =
404 "/xyz/openbmc_project/dump/" +
405 std::string(boost::algorithm::to_lower_copy(dumpType)) +
406 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500407
408 for (auto& object : resp)
409 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500410 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500411 {
412 continue;
413 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800414 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500415 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500416 std::string dumpStatus;
417 nlohmann::json thisEntry;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000418
419 std::string entryID = object.first.filename();
420 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500421 {
422 continue;
423 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500424
425 for (auto& interfaceMap : object.second)
426 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500427 if (interfaceMap.first ==
428 "xyz.openbmc_project.Common.Progress")
429 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800430 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500431 {
432 if (propertyMap.first == "Status")
433 {
Ed Tanous55f79e62022-01-25 11:26:16 -0800434 const auto* status = std::get_if<std::string>(
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500435 &propertyMap.second);
436 if (status == nullptr)
437 {
438 messages::internalError(asyncResp->res);
439 break;
440 }
441 dumpStatus = *status;
442 }
443 }
444 }
445 else if (interfaceMap.first ==
446 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500447 {
448
449 for (auto& propertyMap : interfaceMap.second)
450 {
451 if (propertyMap.first == "Size")
452 {
Ed Tanous55f79e62022-01-25 11:26:16 -0800453 const auto* sizePtr =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500454 std::get_if<uint64_t>(&propertyMap.second);
455 if (sizePtr == nullptr)
456 {
457 messages::internalError(asyncResp->res);
458 break;
459 }
460 size = *sizePtr;
461 break;
462 }
463 }
464 }
465 else if (interfaceMap.first ==
466 "xyz.openbmc_project.Time.EpochTime")
467 {
468
Ed Tanous9eb808c2022-01-25 10:19:23 -0800469 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500470 {
471 if (propertyMap.first == "Elapsed")
472 {
473 const uint64_t* usecsTimeStamp =
474 std::get_if<uint64_t>(&propertyMap.second);
475 if (usecsTimeStamp == nullptr)
476 {
477 messages::internalError(asyncResp->res);
478 break;
479 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800480 timestamp = (*usecsTimeStamp / 1000 / 1000);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500481 break;
482 }
483 }
484 }
485 }
486
George Liu0fda0f12021-11-16 10:06:17 +0800487 if (dumpStatus !=
488 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500489 !dumpStatus.empty())
490 {
491 // Dump status is not Complete, no need to enumerate
492 continue;
493 }
494
George Liu647b3cd2021-07-05 12:43:56 +0800495 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500496 thisEntry["@odata.id"] = dumpPath + entryID;
497 thisEntry["Id"] = entryID;
498 thisEntry["EntryType"] = "Event";
Nan Zhou1d8782e2021-11-29 22:23:18 -0800499 thisEntry["Created"] =
500 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500501 thisEntry["Name"] = dumpType + " Dump Entry";
502
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500503 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500504
505 if (dumpType == "BMC")
506 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500507 thisEntry["DiagnosticDataType"] = "Manager";
508 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500509 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
510 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500511 }
512 else if (dumpType == "System")
513 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500514 thisEntry["DiagnosticDataType"] = "OEM";
515 thisEntry["OEMDiagnosticDataType"] = "System";
516 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500517 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
518 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500519 }
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500520 entriesArray.push_back(std::move(thisEntry));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500521 }
522 asyncResp->res.jsonValue["Members@odata.count"] =
523 entriesArray.size();
524 },
525 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
526 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
527}
528
zhanghch058d1b46d2021-04-01 11:18:24 +0800529inline void
530 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
531 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500532{
533 std::string dumpPath;
534 if (dumpType == "BMC")
535 {
536 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
537 }
538 else if (dumpType == "System")
539 {
540 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
541 }
542 else
543 {
544 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
545 messages::internalError(asyncResp->res);
546 return;
547 }
548
549 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800550 [asyncResp, entryID, dumpPath,
551 dumpType](const boost::system::error_code ec,
552 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500553 if (ec)
554 {
555 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
556 messages::internalError(asyncResp->res);
557 return;
558 }
559
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500560 bool foundDumpEntry = false;
561 std::string dumpEntryPath =
562 "/xyz/openbmc_project/dump/" +
563 std::string(boost::algorithm::to_lower_copy(dumpType)) +
564 "/entry/";
565
Ed Tanous9eb808c2022-01-25 10:19:23 -0800566 for (const auto& objectPath : resp)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500567 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500568 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500569 {
570 continue;
571 }
572
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500573 foundDumpEntry = true;
Nan Zhou1d8782e2021-11-29 22:23:18 -0800574 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500575 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500576 std::string dumpStatus;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500577
Ed Tanous9eb808c2022-01-25 10:19:23 -0800578 for (const auto& interfaceMap : objectPath.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500579 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500580 if (interfaceMap.first ==
581 "xyz.openbmc_project.Common.Progress")
582 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800583 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500584 {
585 if (propertyMap.first == "Status")
586 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800587 const std::string* status =
588 std::get_if<std::string>(
589 &propertyMap.second);
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500590 if (status == nullptr)
591 {
592 messages::internalError(asyncResp->res);
593 break;
594 }
595 dumpStatus = *status;
596 }
597 }
598 }
599 else if (interfaceMap.first ==
600 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500601 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800602 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500603 {
604 if (propertyMap.first == "Size")
605 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800606 const uint64_t* sizePtr =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500607 std::get_if<uint64_t>(&propertyMap.second);
608 if (sizePtr == nullptr)
609 {
610 messages::internalError(asyncResp->res);
611 break;
612 }
613 size = *sizePtr;
614 break;
615 }
616 }
617 }
618 else if (interfaceMap.first ==
619 "xyz.openbmc_project.Time.EpochTime")
620 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800621 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500622 {
623 if (propertyMap.first == "Elapsed")
624 {
625 const uint64_t* usecsTimeStamp =
626 std::get_if<uint64_t>(&propertyMap.second);
627 if (usecsTimeStamp == nullptr)
628 {
629 messages::internalError(asyncResp->res);
630 break;
631 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800632 timestamp = *usecsTimeStamp / 1000 / 1000;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500633 break;
634 }
635 }
636 }
637 }
638
George Liu0fda0f12021-11-16 10:06:17 +0800639 if (dumpStatus !=
640 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500641 !dumpStatus.empty())
642 {
643 // Dump status is not Complete
644 // return not found until status is changed to Completed
645 messages::resourceNotFound(asyncResp->res,
646 dumpType + " dump", entryID);
647 return;
648 }
649
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500650 asyncResp->res.jsonValue["@odata.type"] =
George Liu647b3cd2021-07-05 12:43:56 +0800651 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500652 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
653 asyncResp->res.jsonValue["Id"] = entryID;
654 asyncResp->res.jsonValue["EntryType"] = "Event";
655 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800656 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500657 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
658
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500659 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500660
661 if (dumpType == "BMC")
662 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500663 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
664 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500665 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
666 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500667 }
668 else if (dumpType == "System")
669 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500670 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
671 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500672 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500673 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500674 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
675 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500676 }
677 }
Ed Tanouse05aec52022-01-25 10:28:56 -0800678 if (!foundDumpEntry)
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500679 {
680 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
681 messages::internalError(asyncResp->res);
682 return;
683 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500684 },
685 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
686 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
687}
688
zhanghch058d1b46d2021-04-01 11:18:24 +0800689inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800690 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500691 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500692{
George Liu3de8d8b2021-03-22 17:49:39 +0800693 auto respHandler = [asyncResp,
694 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500695 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
696 if (ec)
697 {
George Liu3de8d8b2021-03-22 17:49:39 +0800698 if (ec.value() == EBADR)
699 {
700 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
701 return;
702 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500703 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
704 << ec;
705 messages::internalError(asyncResp->res);
706 return;
707 }
708 };
709 crow::connections::systemBus->async_method_call(
710 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500711 "/xyz/openbmc_project/dump/" +
712 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
713 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500714 "xyz.openbmc_project.Object.Delete", "Delete");
715}
716
zhanghch058d1b46d2021-04-01 11:18:24 +0800717inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700718 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800719 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
720 const uint32_t& dumpId, const std::string& dumpPath,
721 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500722{
723 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500724 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500725 boost::system::error_code err, sdbusplus::message::message& m,
726 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000727 if (err)
728 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500729 BMCWEB_LOG_ERROR << "Error in creating a dump";
730 taskData->state = "Cancelled";
731 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000732 }
Ed Tanousb9d36b42022-02-26 21:42:46 -0800733
734 dbus::utility::DBusInteracesMap interfacesList;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500735
736 sdbusplus::message::object_path objPath;
737
738 m.read(objPath, interfacesList);
739
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500740 if (objPath.str ==
741 "/xyz/openbmc_project/dump/" +
742 std::string(boost::algorithm::to_lower_copy(dumpType)) +
743 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500744 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500745 nlohmann::json retMessage = messages::success();
746 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500747
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500748 std::string headerLoc =
749 "Location: " + dumpPath + std::to_string(dumpId);
750 taskData->payload->httpHeaders.emplace_back(
751 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500752
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500753 taskData->state = "Completed";
754 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500755 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500756 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500757 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800758 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500759 "member='InterfacesAdded', "
760 "path='/xyz/openbmc_project/dump'");
761
762 task->startTimer(std::chrono::minutes(3));
763 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700764 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500765}
766
zhanghch058d1b46d2021-04-01 11:18:24 +0800767inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
768 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500769{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500770
771 std::string dumpPath;
772 if (dumpType == "BMC")
773 {
774 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
775 }
776 else if (dumpType == "System")
777 {
778 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
779 }
780 else
781 {
782 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
783 messages::internalError(asyncResp->res);
784 return;
785 }
786
787 std::optional<std::string> diagnosticDataType;
788 std::optional<std::string> oemDiagnosticDataType;
789
Willy Tu15ed6782021-12-14 11:03:16 -0800790 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500791 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
792 "OEMDiagnosticDataType", oemDiagnosticDataType))
793 {
794 return;
795 }
796
797 if (dumpType == "System")
798 {
799 if (!oemDiagnosticDataType || !diagnosticDataType)
800 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800801 BMCWEB_LOG_ERROR
802 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500803 messages::actionParameterMissing(
804 asyncResp->res, "CollectDiagnosticData",
805 "DiagnosticDataType & OEMDiagnosticDataType");
806 return;
807 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700808 if ((*oemDiagnosticDataType != "System") ||
809 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500810 {
811 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700812 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500813 return;
814 }
815 }
816 else if (dumpType == "BMC")
817 {
818 if (!diagnosticDataType)
819 {
George Liu0fda0f12021-11-16 10:06:17 +0800820 BMCWEB_LOG_ERROR
821 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500822 messages::actionParameterMissing(
823 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
824 return;
825 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700826 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500827 {
828 BMCWEB_LOG_ERROR
829 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700830 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500831 return;
832 }
833 }
834
835 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700836 [asyncResp, payload(task::Payload(req)), dumpPath,
837 dumpType](const boost::system::error_code ec,
838 const uint32_t& dumpId) mutable {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500839 if (ec)
840 {
841 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
842 messages::internalError(asyncResp->res);
843 return;
844 }
845 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
846
Ed Tanous98be3e32021-09-16 15:05:36 -0700847 createDumpTaskCallback(std::move(payload), asyncResp, dumpId,
848 dumpPath, dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500849 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500850 "xyz.openbmc_project.Dump.Manager",
851 "/xyz/openbmc_project/dump/" +
852 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500853 "xyz.openbmc_project.Dump.Create", "CreateDump");
854}
855
zhanghch058d1b46d2021-04-01 11:18:24 +0800856inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
857 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500858{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500859 std::string dumpTypeLowerCopy =
860 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800861
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500862 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800863 [asyncResp, dumpType](
864 const boost::system::error_code ec,
865 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500866 if (ec)
867 {
868 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
869 messages::internalError(asyncResp->res);
870 return;
871 }
872
873 for (const std::string& path : subTreePaths)
874 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000875 sdbusplus::message::object_path objPath(path);
876 std::string logID = objPath.filename();
877 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500878 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000879 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500880 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000881 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500882 }
883 },
884 "xyz.openbmc_project.ObjectMapper",
885 "/xyz/openbmc_project/object_mapper",
886 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500887 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
888 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
889 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500890}
891
Ed Tanousb9d36b42022-02-26 21:42:46 -0800892inline static void
893 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
894 std::string& filename, std::string& timestamp,
895 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700896{
897 for (auto property : params)
898 {
899 if (property.first == "Timestamp")
900 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500901 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500902 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700903 if (value != nullptr)
904 {
905 timestamp = *value;
906 }
907 }
908 else if (property.first == "Filename")
909 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500910 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500911 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700912 if (value != nullptr)
913 {
914 filename = *value;
915 }
916 }
917 else if (property.first == "Log")
918 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500919 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500920 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700921 if (value != nullptr)
922 {
923 logfile = *value;
924 }
925 }
926 }
927}
928
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500929constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700930inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700931{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800932 /**
933 * Functions triggers appropriate requests on DBus
934 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700935 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700936 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700937 .methods(boost::beast::http::verb::get)(
938 [](const crow::Request&,
939 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
940
941 {
942 // Collections don't include the static data added by SubRoute
943 // because it has a duplicate entry for members
944 asyncResp->res.jsonValue["@odata.type"] =
945 "#LogServiceCollection.LogServiceCollection";
946 asyncResp->res.jsonValue["@odata.id"] =
947 "/redfish/v1/Systems/system/LogServices";
948 asyncResp->res.jsonValue["Name"] =
949 "System Log Services Collection";
950 asyncResp->res.jsonValue["Description"] =
951 "Collection of LogServices for this Computer System";
952 nlohmann::json& logServiceArray =
953 asyncResp->res.jsonValue["Members"];
954 logServiceArray = nlohmann::json::array();
955 logServiceArray.push_back(
956 {{"@odata.id",
957 "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500958#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700959 logServiceArray.push_back(
960 {{"@odata.id",
961 "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600962#endif
963
Jason M. Billsd53dd412019-02-12 17:16:22 -0800964#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700965 logServiceArray.push_back(
966 {{"@odata.id",
967 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800968#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800969
970#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
971 logServiceArray.push_back(
972 {{"@odata.id",
973 "/redfish/v1/Systems/system/LogServices/HostLogger"}});
974#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700975 asyncResp->res.jsonValue["Members@odata.count"] =
976 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800977
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700978 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800979 [asyncResp](
980 const boost::system::error_code ec,
981 const dbus::utility::MapperGetSubTreePathsResponse&
982 subtreePath) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700983 if (ec)
984 {
985 BMCWEB_LOG_ERROR << ec;
986 return;
987 }
ZhikuiRena3316fc2020-01-29 14:58:08 -0800988
Ed Tanous55f79e62022-01-25 11:26:16 -0800989 for (const auto& pathStr : subtreePath)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700990 {
991 if (pathStr.find("PostCode") != std::string::npos)
992 {
993 nlohmann::json& logServiceArrayLocal =
994 asyncResp->res.jsonValue["Members"];
995 logServiceArrayLocal.push_back(
George Liu0fda0f12021-11-16 10:06:17 +0800996 {{"@odata.id",
997 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700998 asyncResp->res
999 .jsonValue["Members@odata.count"] =
1000 logServiceArrayLocal.size();
1001 return;
1002 }
1003 }
1004 },
1005 "xyz.openbmc_project.ObjectMapper",
1006 "/xyz/openbmc_project/object_mapper",
1007 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
1008 0, std::array<const char*, 1>{postCodeIface});
1009 });
1010}
1011
1012inline void requestRoutesEventLogService(App& app)
1013{
1014 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -07001015 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 .methods(
1017 boost::beast::http::verb::
1018 get)([](const crow::Request&,
1019 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1020 asyncResp->res.jsonValue["@odata.id"] =
1021 "/redfish/v1/Systems/system/LogServices/EventLog";
1022 asyncResp->res.jsonValue["@odata.type"] =
1023 "#LogService.v1_1_0.LogService";
1024 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1025 asyncResp->res.jsonValue["Description"] =
1026 "System Event Log Service";
1027 asyncResp->res.jsonValue["Id"] = "EventLog";
1028 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301029
1030 std::pair<std::string, std::string> redfishDateTimeOffset =
1031 crow::utility::getDateTimeOffsetNow();
1032
1033 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1034 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1035 redfishDateTimeOffset.second;
1036
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001037 asyncResp->res.jsonValue["Entries"] = {
1038 {"@odata.id",
1039 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1040 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1041
George Liu0fda0f12021-11-16 10:06:17 +08001042 {"target",
1043 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001044 });
1045}
1046
1047inline void requestRoutesJournalEventLogClear(App& app)
1048{
Jason M. Bills4978b632022-02-22 14:17:43 -08001049 BMCWEB_ROUTE(
1050 app,
1051 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001052 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001053 .methods(boost::beast::http::verb::post)(
1054 [](const crow::Request&,
1055 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1056 // Clear the EventLog by deleting the log files
1057 std::vector<std::filesystem::path> redfishLogFiles;
1058 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001059 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001060 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001061 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001062 std::error_code ec;
1063 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001064 }
1065 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001066
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001067 // Reload rsyslog so it knows to start new log files
1068 crow::connections::systemBus->async_method_call(
1069 [asyncResp](const boost::system::error_code ec) {
1070 if (ec)
1071 {
1072 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1073 << ec;
1074 messages::internalError(asyncResp->res);
1075 return;
1076 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001077
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001078 messages::success(asyncResp->res);
1079 },
1080 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1081 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1082 "rsyslog.service", "replace");
1083 });
1084}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001085
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001086static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001087 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001088 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001089{
Jason M. Bills95820182019-04-22 16:25:34 -07001090 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001091 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001092 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001093 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001094 {
1095 return 1;
1096 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001097 std::string timestamp = logEntry.substr(0, space);
1098 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001099 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001100 if (entryStart == std::string::npos)
1101 {
1102 return 1;
1103 }
1104 std::string_view entry(logEntry);
1105 entry.remove_prefix(entryStart);
1106 // Use split to separate the entry into its fields
1107 std::vector<std::string> logEntryFields;
1108 boost::split(logEntryFields, entry, boost::is_any_of(","),
1109 boost::token_compress_on);
1110 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001111 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001112 {
1113 return 1;
1114 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001115 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001116
Jason M. Bills4851d452019-03-28 11:27:48 -07001117 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001118 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001119
Jason M. Bills4851d452019-03-28 11:27:48 -07001120 std::string msg;
1121 std::string severity;
1122 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001123 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001124 msg = message->message;
Ed Tanous5f2b84e2022-02-08 00:41:53 -08001125 severity = message->messageSeverity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001126 }
1127
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001128 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001129 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001130 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001131 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001132 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001133 // If the first string is empty, assume there are no MessageArgs
1134 std::size_t messageArgsSize = 0;
1135 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001136 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001137 messageArgsSize = logEntryFields.size() - 1;
1138 }
1139
Ed Tanous23a21a12020-07-25 04:45:05 +00001140 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001141
1142 // Fill the MessageArgs into the Message
1143 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001144 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001145 {
1146 std::string argStr = "%" + std::to_string(++i);
1147 size_t argPos = msg.find(argStr);
1148 if (argPos != std::string::npos)
1149 {
1150 msg.replace(argPos, argStr.length(), messageArg);
1151 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001152 }
1153 }
1154
Jason M. Bills95820182019-04-22 16:25:34 -07001155 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1156 // format which matches the Redfish format except for the fractional seconds
1157 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001158 std::size_t dot = timestamp.find_first_of('.');
1159 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001160 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001161 {
Jason M. Bills95820182019-04-22 16:25:34 -07001162 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001163 }
1164
1165 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001166 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001167 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001168 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001169 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001170 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001171 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001172 {"Id", logEntryID},
1173 {"Message", std::move(msg)},
1174 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001175 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001176 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001177 {"Severity", std::move(severity)},
1178 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001179 return 0;
1180}
1181
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001182inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001183{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001184 BMCWEB_ROUTE(app,
1185 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001186 .privileges(redfish::privileges::getLogEntryCollection)
Jason M. Bills4978b632022-02-22 14:17:43 -08001187 .methods(
1188 boost::beast::http::verb::
1189 get)([](const crow::Request& req,
1190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1191 uint64_t skip = 0;
1192 uint64_t top = maxEntriesPerPage; // Show max entries by default
1193 if (!getSkipParam(asyncResp, req, skip))
1194 {
1195 return;
1196 }
1197 if (!getTopParam(asyncResp, req, top))
1198 {
1199 return;
1200 }
1201 // Collections don't include the static data added by SubRoute
1202 // because it has a duplicate entry for members
1203 asyncResp->res.jsonValue["@odata.type"] =
1204 "#LogEntryCollection.LogEntryCollection";
1205 asyncResp->res.jsonValue["@odata.id"] =
1206 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1207 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1208 asyncResp->res.jsonValue["Description"] =
1209 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001210
Jason M. Bills4978b632022-02-22 14:17:43 -08001211 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1212 logEntryArray = nlohmann::json::array();
1213 // Go through the log files and create a unique ID for each
1214 // entry
1215 std::vector<std::filesystem::path> redfishLogFiles;
1216 getRedfishLogFiles(redfishLogFiles);
1217 uint64_t entryCount = 0;
1218 std::string logEntry;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001219
Jason M. Bills4978b632022-02-22 14:17:43 -08001220 // Oldest logs are in the last file, so start there and loop
1221 // backwards
1222 for (auto it = redfishLogFiles.rbegin();
1223 it < redfishLogFiles.rend(); it++)
1224 {
1225 std::ifstream logStream(*it);
1226 if (!logStream.is_open())
Andrew Geisslercb92c032018-08-17 07:56:14 -07001227 {
Jason M. Bills4978b632022-02-22 14:17:43 -08001228 continue;
1229 }
1230
1231 // Reset the unique ID on the first entry
1232 bool firstEntry = true;
1233 while (std::getline(logStream, logEntry))
1234 {
1235 entryCount++;
1236 // Handle paging using skip (number of entries to skip
1237 // from the start) and top (number of entries to
1238 // display)
1239 if (entryCount <= skip || entryCount > skip + top)
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001240 {
1241 continue;
1242 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001243
Jason M. Bills4978b632022-02-22 14:17:43 -08001244 std::string idStr;
1245 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001246 {
Jason M. Bills4978b632022-02-22 14:17:43 -08001247 continue;
1248 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001249
Jason M. Bills4978b632022-02-22 14:17:43 -08001250 if (firstEntry)
1251 {
1252 firstEntry = false;
1253 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001254
Jason M. Bills4978b632022-02-22 14:17:43 -08001255 logEntryArray.push_back({});
1256 nlohmann::json& bmcLogEntry = logEntryArray.back();
1257 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) !=
1258 0)
1259 {
1260 messages::internalError(asyncResp->res);
1261 return;
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001262 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001263 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001264 }
1265 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1266 if (skip + top < entryCount)
1267 {
1268 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1269 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
1270 std::to_string(skip + top);
1271 }
1272 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001273}
Chicago Duan336e96c2019-07-15 14:22:08 +08001274
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001275inline void requestRoutesJournalEventLogEntry(App& app)
1276{
1277 BMCWEB_ROUTE(
1278 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001279 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001280 .methods(boost::beast::http::verb::get)(
1281 [](const crow::Request&,
1282 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1283 const std::string& param) {
1284 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001285
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001286 // Go through the log files and check the unique ID for each
1287 // entry to find the target entry
1288 std::vector<std::filesystem::path> redfishLogFiles;
1289 getRedfishLogFiles(redfishLogFiles);
1290 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001291
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001292 // Oldest logs are in the last file, so start there and loop
1293 // backwards
1294 for (auto it = redfishLogFiles.rbegin();
1295 it < redfishLogFiles.rend(); it++)
1296 {
1297 std::ifstream logStream(*it);
1298 if (!logStream.is_open())
1299 {
1300 continue;
1301 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001302
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001303 // Reset the unique ID on the first entry
1304 bool firstEntry = true;
1305 while (std::getline(logStream, logEntry))
1306 {
1307 std::string idStr;
1308 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1309 {
1310 continue;
1311 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001312
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001313 if (firstEntry)
1314 {
1315 firstEntry = false;
1316 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001317
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001318 if (idStr == targetID)
1319 {
1320 if (fillEventLogEntryJson(
1321 idStr, logEntry,
1322 asyncResp->res.jsonValue) != 0)
1323 {
1324 messages::internalError(asyncResp->res);
1325 return;
1326 }
1327 return;
1328 }
1329 }
1330 }
1331 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07001332 messages::resourceMissingAtURI(
1333 asyncResp->res, crow::utility::urlFromPieces(targetID));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001334 });
1335}
1336
1337inline void requestRoutesDBusEventLogEntryCollection(App& app)
1338{
1339 BMCWEB_ROUTE(app,
1340 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001341 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001342 .methods(
1343 boost::beast::http::verb::
1344 get)([](const crow::Request&,
1345 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
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
Xiaochao Ma75710de2021-01-21 17:56:02 +08001358 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001359 [asyncResp](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001360 const dbus::utility::ManagedObjectType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001361 if (ec)
1362 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001363 // TODO Handle for specific error code
1364 BMCWEB_LOG_ERROR
1365 << "getLogEntriesIfaceData resp_handler got error "
1366 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001367 messages::internalError(asyncResp->res);
1368 return;
1369 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001370 nlohmann::json& entriesArray =
1371 asyncResp->res.jsonValue["Members"];
1372 entriesArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -08001373 for (const auto& objectPath : resp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001374 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001375 const uint32_t* id = nullptr;
Ed Tanousc419c752022-01-26 12:19:54 -08001376 const uint64_t* timestamp = nullptr;
1377 const uint64_t* updateTimestamp = nullptr;
Ed Tanous914e2d52022-01-07 11:38:34 -08001378 const std::string* severity = nullptr;
1379 const std::string* message = nullptr;
1380 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001381 bool resolved = false;
Ed Tanous9eb808c2022-01-25 10:19:23 -08001382 for (const auto& interfaceMap : objectPath.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001383 {
1384 if (interfaceMap.first ==
1385 "xyz.openbmc_project.Logging.Entry")
1386 {
Ed Tanous9eb808c2022-01-25 10:19:23 -08001387 for (const auto& propertyMap :
1388 interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001389 {
1390 if (propertyMap.first == "Id")
1391 {
1392 id = std::get_if<uint32_t>(
1393 &propertyMap.second);
1394 }
1395 else if (propertyMap.first == "Timestamp")
1396 {
Ed Tanousc419c752022-01-26 12:19:54 -08001397 timestamp = std::get_if<uint64_t>(
1398 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001399 }
1400 else if (propertyMap.first ==
1401 "UpdateTimestamp")
1402 {
Ed Tanousc419c752022-01-26 12:19:54 -08001403 updateTimestamp = std::get_if<uint64_t>(
1404 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001405 }
1406 else if (propertyMap.first == "Severity")
1407 {
1408 severity = std::get_if<std::string>(
1409 &propertyMap.second);
1410 }
1411 else if (propertyMap.first == "Message")
1412 {
1413 message = std::get_if<std::string>(
1414 &propertyMap.second);
1415 }
1416 else if (propertyMap.first == "Resolved")
1417 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001418 const bool* resolveptr =
1419 std::get_if<bool>(
1420 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001421 if (resolveptr == nullptr)
1422 {
1423 messages::internalError(
1424 asyncResp->res);
1425 return;
1426 }
1427 resolved = *resolveptr;
1428 }
1429 }
1430 if (id == nullptr || message == nullptr ||
1431 severity == nullptr)
1432 {
1433 messages::internalError(asyncResp->res);
1434 return;
1435 }
1436 }
1437 else if (interfaceMap.first ==
1438 "xyz.openbmc_project.Common.FilePath")
1439 {
Ed Tanous9eb808c2022-01-25 10:19:23 -08001440 for (const auto& propertyMap :
1441 interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001442 {
1443 if (propertyMap.first == "Path")
1444 {
1445 filePath = std::get_if<std::string>(
1446 &propertyMap.second);
1447 }
1448 }
1449 }
1450 }
1451 // Object path without the
1452 // xyz.openbmc_project.Logging.Entry interface, ignore
1453 // and continue.
1454 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001455 severity == nullptr || timestamp == nullptr ||
1456 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001457 {
1458 continue;
1459 }
1460 entriesArray.push_back({});
1461 nlohmann::json& thisEntry = entriesArray.back();
1462 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1463 thisEntry["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001464 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001465 std::to_string(*id);
1466 thisEntry["Name"] = "System Event Log Entry";
1467 thisEntry["Id"] = std::to_string(*id);
1468 thisEntry["Message"] = *message;
1469 thisEntry["Resolved"] = resolved;
1470 thisEntry["EntryType"] = "Event";
1471 thisEntry["Severity"] =
1472 translateSeverityDbusToRedfish(*severity);
1473 thisEntry["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001474 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001475 thisEntry["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001476 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001477 if (filePath != nullptr)
1478 {
1479 thisEntry["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001480 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001481 std::to_string(*id) + "/attachment";
1482 }
1483 }
1484 std::sort(entriesArray.begin(), entriesArray.end(),
1485 [](const nlohmann::json& left,
1486 const nlohmann::json& right) {
1487 return (left["Id"] <= right["Id"]);
1488 });
1489 asyncResp->res.jsonValue["Members@odata.count"] =
1490 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001491 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001492 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1493 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1494 });
1495}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001496
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001497inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001498{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001499 BMCWEB_ROUTE(
1500 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001501 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001502 .methods(boost::beast::http::verb::get)(
1503 [](const crow::Request&,
1504 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1505 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001506
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001507 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001508 std::string entryID = param;
1509 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001510
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001511 // DBus implementation of EventLog/Entries
1512 // Make call to Logging Service to find all log entry objects
1513 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -08001514 [asyncResp,
1515 entryID](const boost::system::error_code ec,
1516 const dbus::utility::DBusPropertiesMap& resp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001517 if (ec.value() == EBADR)
1518 {
1519 messages::resourceNotFound(
1520 asyncResp->res, "EventLogEntry", entryID);
1521 return;
1522 }
1523 if (ec)
1524 {
George Liu0fda0f12021-11-16 10:06:17 +08001525 BMCWEB_LOG_ERROR
1526 << "EventLogEntry (DBus) resp_handler got error "
1527 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001528 messages::internalError(asyncResp->res);
1529 return;
1530 }
Ed Tanous914e2d52022-01-07 11:38:34 -08001531 const uint32_t* id = nullptr;
Ed Tanousc419c752022-01-26 12:19:54 -08001532 const uint64_t* timestamp = nullptr;
1533 const uint64_t* updateTimestamp = nullptr;
Ed Tanous914e2d52022-01-07 11:38:34 -08001534 const std::string* severity = nullptr;
1535 const std::string* message = nullptr;
1536 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001537 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001538
Ed Tanous9eb808c2022-01-25 10:19:23 -08001539 for (const auto& propertyMap : resp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001540 {
1541 if (propertyMap.first == "Id")
1542 {
1543 id = std::get_if<uint32_t>(&propertyMap.second);
1544 }
1545 else if (propertyMap.first == "Timestamp")
1546 {
Ed Tanousc419c752022-01-26 12:19:54 -08001547 timestamp =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001548 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001549 }
1550 else if (propertyMap.first == "UpdateTimestamp")
1551 {
Ed Tanousc419c752022-01-26 12:19:54 -08001552 updateTimestamp =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001553 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001554 }
1555 else if (propertyMap.first == "Severity")
1556 {
1557 severity = std::get_if<std::string>(
1558 &propertyMap.second);
1559 }
1560 else if (propertyMap.first == "Message")
1561 {
1562 message = std::get_if<std::string>(
1563 &propertyMap.second);
1564 }
1565 else if (propertyMap.first == "Resolved")
1566 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001567 const bool* resolveptr =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001568 std::get_if<bool>(&propertyMap.second);
1569 if (resolveptr == nullptr)
1570 {
1571 messages::internalError(asyncResp->res);
1572 return;
1573 }
1574 resolved = *resolveptr;
1575 }
1576 else if (propertyMap.first == "Path")
1577 {
1578 filePath = std::get_if<std::string>(
1579 &propertyMap.second);
1580 }
1581 }
1582 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001583 severity == nullptr || timestamp == nullptr ||
1584 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001585 {
1586 messages::internalError(asyncResp->res);
1587 return;
1588 }
1589 asyncResp->res.jsonValue["@odata.type"] =
1590 "#LogEntry.v1_8_0.LogEntry";
1591 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001592 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001593 std::to_string(*id);
1594 asyncResp->res.jsonValue["Name"] =
1595 "System Event Log Entry";
1596 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1597 asyncResp->res.jsonValue["Message"] = *message;
1598 asyncResp->res.jsonValue["Resolved"] = resolved;
1599 asyncResp->res.jsonValue["EntryType"] = "Event";
1600 asyncResp->res.jsonValue["Severity"] =
1601 translateSeverityDbusToRedfish(*severity);
1602 asyncResp->res.jsonValue["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001603 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001604 asyncResp->res.jsonValue["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001605 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001606 if (filePath != nullptr)
1607 {
1608 asyncResp->res.jsonValue["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001609 "/redfish/v1/Systems/system/LogServices/EventLog/attachment/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001610 std::to_string(*id);
1611 }
1612 },
1613 "xyz.openbmc_project.Logging",
1614 "/xyz/openbmc_project/logging/entry/" + entryID,
1615 "org.freedesktop.DBus.Properties", "GetAll", "");
1616 });
1617
1618 BMCWEB_ROUTE(
1619 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001620 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001621 .methods(boost::beast::http::verb::patch)(
1622 [](const crow::Request& req,
1623 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1624 const std::string& entryId) {
1625 std::optional<bool> resolved;
1626
Willy Tu15ed6782021-12-14 11:03:16 -08001627 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1628 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001629 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001630 return;
1631 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001632 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001633
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001634 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001635 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001636 if (ec)
1637 {
1638 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1639 messages::internalError(asyncResp->res);
1640 return;
1641 }
1642 },
1643 "xyz.openbmc_project.Logging",
1644 "/xyz/openbmc_project/logging/entry/" + entryId,
1645 "org.freedesktop.DBus.Properties", "Set",
1646 "xyz.openbmc_project.Logging.Entry", "Resolved",
Ed Tanous168e20c2021-12-13 14:39:53 -08001647 dbus::utility::DbusVariantType(*resolved));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001648 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001649
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001650 BMCWEB_ROUTE(
1651 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001652 .privileges(redfish::privileges::deleteLogEntry)
1653
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001654 .methods(boost::beast::http::verb::delete_)(
1655 [](const crow::Request&,
1656 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1657 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001658
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001659 {
1660 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001661
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001662 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001663
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001664 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001665
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001666 // Process response from Logging service.
1667 auto respHandler = [asyncResp, entryID](
1668 const boost::system::error_code ec) {
1669 BMCWEB_LOG_DEBUG
1670 << "EventLogEntry (DBus) doDelete callback: Done";
1671 if (ec)
1672 {
1673 if (ec.value() == EBADR)
1674 {
1675 messages::resourceNotFound(asyncResp->res,
1676 "LogEntry", entryID);
1677 return;
1678 }
1679 // TODO Handle for specific error code
George Liu0fda0f12021-11-16 10:06:17 +08001680 BMCWEB_LOG_ERROR
1681 << "EventLogEntry (DBus) doDelete respHandler got error "
1682 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001683 asyncResp->res.result(
1684 boost::beast::http::status::internal_server_error);
1685 return;
1686 }
1687
1688 asyncResp->res.result(boost::beast::http::status::ok);
1689 };
1690
1691 // Make call to Logging service to request Delete Log
1692 crow::connections::systemBus->async_method_call(
1693 respHandler, "xyz.openbmc_project.Logging",
1694 "/xyz/openbmc_project/logging/entry/" + entryID,
1695 "xyz.openbmc_project.Object.Delete", "Delete");
1696 });
1697}
1698
1699inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001700{
George Liu0fda0f12021-11-16 10:06:17 +08001701 BMCWEB_ROUTE(
1702 app,
1703 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001704 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001705 .methods(boost::beast::http::verb::get)(
1706 [](const crow::Request& req,
1707 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1708 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001709
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001710 {
George Liu647b3cd2021-07-05 12:43:56 +08001711 if (!http_helpers::isOctetAccepted(
1712 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001713 {
1714 asyncResp->res.result(
1715 boost::beast::http::status::bad_request);
1716 return;
1717 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001718
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001719 std::string entryID = param;
1720 dbus::utility::escapePathForDbus(entryID);
1721
1722 crow::connections::systemBus->async_method_call(
1723 [asyncResp,
1724 entryID](const boost::system::error_code ec,
1725 const sdbusplus::message::unix_fd& unixfd) {
1726 if (ec.value() == EBADR)
1727 {
1728 messages::resourceNotFound(
1729 asyncResp->res, "EventLogAttachment", entryID);
1730 return;
1731 }
1732 if (ec)
1733 {
1734 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1735 messages::internalError(asyncResp->res);
1736 return;
1737 }
1738
1739 int fd = -1;
1740 fd = dup(unixfd);
1741 if (fd == -1)
1742 {
1743 messages::internalError(asyncResp->res);
1744 return;
1745 }
1746
1747 long long int size = lseek(fd, 0, SEEK_END);
1748 if (size == -1)
1749 {
1750 messages::internalError(asyncResp->res);
1751 return;
1752 }
1753
1754 // Arbitrary max size of 64kb
1755 constexpr int maxFileSize = 65536;
1756 if (size > maxFileSize)
1757 {
1758 BMCWEB_LOG_ERROR
1759 << "File size exceeds maximum allowed size of "
1760 << maxFileSize;
1761 messages::internalError(asyncResp->res);
1762 return;
1763 }
1764 std::vector<char> data(static_cast<size_t>(size));
1765 long long int rc = lseek(fd, 0, SEEK_SET);
1766 if (rc == -1)
1767 {
1768 messages::internalError(asyncResp->res);
1769 return;
1770 }
1771 rc = read(fd, data.data(), data.size());
1772 if ((rc == -1) || (rc != size))
1773 {
1774 messages::internalError(asyncResp->res);
1775 return;
1776 }
1777 close(fd);
1778
1779 std::string_view strData(data.data(), data.size());
1780 std::string output =
1781 crow::utility::base64encode(strData);
1782
1783 asyncResp->res.addHeader("Content-Type",
1784 "application/octet-stream");
1785 asyncResp->res.addHeader("Content-Transfer-Encoding",
1786 "Base64");
1787 asyncResp->res.body() = std::move(output);
1788 },
1789 "xyz.openbmc_project.Logging",
1790 "/xyz/openbmc_project/logging/entry/" + entryID,
1791 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1792 });
1793}
1794
Spencer Kub7028eb2021-10-26 15:27:35 +08001795constexpr const char* hostLoggerFolderPath = "/var/log/console";
1796
1797inline bool
1798 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1799 std::vector<std::filesystem::path>& hostLoggerFiles)
1800{
1801 std::error_code ec;
1802 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1803 if (ec)
1804 {
1805 BMCWEB_LOG_ERROR << ec.message();
1806 return false;
1807 }
1808 for (const std::filesystem::directory_entry& it : logPath)
1809 {
1810 std::string filename = it.path().filename();
1811 // Prefix of each log files is "log". Find the file and save the
1812 // path
1813 if (boost::starts_with(filename, "log"))
1814 {
1815 hostLoggerFiles.emplace_back(it.path());
1816 }
1817 }
1818 // As the log files rotate, they are appended with a ".#" that is higher for
1819 // the older logs. Since we start from oldest logs, sort the name in
1820 // descending order.
1821 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1822 AlphanumLess<std::string>());
1823
1824 return true;
1825}
1826
1827inline bool
1828 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
1829 uint64_t& skip, uint64_t& top,
1830 std::vector<std::string>& logEntries, size_t& logCount)
1831{
1832 GzFileReader logFile;
1833
1834 // Go though all log files and expose host logs.
1835 for (const std::filesystem::path& it : hostLoggerFiles)
1836 {
1837 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1838 {
1839 BMCWEB_LOG_ERROR << "fail to expose host logs";
1840 return false;
1841 }
1842 }
1843 // Get lastMessage from constructor by getter
1844 std::string lastMessage = logFile.getLastMessage();
1845 if (!lastMessage.empty())
1846 {
1847 logCount++;
1848 if (logCount > skip && logCount <= (skip + top))
1849 {
1850 logEntries.push_back(lastMessage);
1851 }
1852 }
1853 return true;
1854}
1855
1856inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1857 const std::string& msg,
1858 nlohmann::json& logEntryJson)
1859{
1860 // Fill in the log entry with the gathered data.
1861 logEntryJson = {
1862 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1863 {"@odata.id",
1864 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1865 logEntryID},
1866 {"Name", "Host Logger Entry"},
1867 {"Id", logEntryID},
1868 {"Message", msg},
1869 {"EntryType", "Oem"},
1870 {"Severity", "OK"},
1871 {"OemRecordFormat", "Host Logger Entry"}};
1872}
1873
1874inline void requestRoutesSystemHostLogger(App& app)
1875{
1876 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1877 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08001878 .methods(
1879 boost::beast::http::verb::
1880 get)([](const crow::Request&,
1881 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1882 asyncResp->res.jsonValue["@odata.id"] =
1883 "/redfish/v1/Systems/system/LogServices/HostLogger";
1884 asyncResp->res.jsonValue["@odata.type"] =
1885 "#LogService.v1_1_0.LogService";
1886 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1887 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1888 asyncResp->res.jsonValue["Id"] = "HostLogger";
1889 asyncResp->res.jsonValue["Entries"] = {
1890 {"@odata.id",
1891 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries"}};
1892 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001893}
1894
1895inline void requestRoutesSystemHostLoggerCollection(App& app)
1896{
1897 BMCWEB_ROUTE(app,
1898 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1899 .privileges(redfish::privileges::getLogEntry)
George Liu0fda0f12021-11-16 10:06:17 +08001900 .methods(
1901 boost::beast::http::verb::
1902 get)([](const crow::Request& req,
1903 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1904 uint64_t skip = 0;
1905 uint64_t top = maxEntriesPerPage; // Show max 1000 entries by
1906 // default, allow range 1 to
1907 // 1000 entries per page.
1908 if (!getSkipParam(asyncResp, req, skip))
1909 {
1910 return;
1911 }
1912 if (!getTopParam(asyncResp, req, top))
1913 {
1914 return;
1915 }
1916 asyncResp->res.jsonValue["@odata.id"] =
1917 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1918 asyncResp->res.jsonValue["@odata.type"] =
1919 "#LogEntryCollection.LogEntryCollection";
1920 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1921 asyncResp->res.jsonValue["Description"] =
1922 "Collection of HostLogger Entries";
1923 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1924 logEntryArray = nlohmann::json::array();
1925 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001926
George Liu0fda0f12021-11-16 10:06:17 +08001927 std::vector<std::filesystem::path> hostLoggerFiles;
1928 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1929 {
1930 BMCWEB_LOG_ERROR << "fail to get host log file path";
1931 return;
1932 }
1933
1934 size_t logCount = 0;
1935 // This vector only store the entries we want to expose that
1936 // control by skip and top.
1937 std::vector<std::string> logEntries;
1938 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1939 logCount))
1940 {
1941 messages::internalError(asyncResp->res);
1942 return;
1943 }
1944 // If vector is empty, that means skip value larger than total
1945 // log count
Ed Tanous26f69762022-01-25 09:49:11 -08001946 if (logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001947 {
1948 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1949 return;
1950 }
Ed Tanous26f69762022-01-25 09:49:11 -08001951 if (!logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001952 {
1953 for (size_t i = 0; i < logEntries.size(); i++)
Spencer Kub7028eb2021-10-26 15:27:35 +08001954 {
George Liu0fda0f12021-11-16 10:06:17 +08001955 logEntryArray.push_back({});
1956 nlohmann::json& hostLogEntry = logEntryArray.back();
1957 fillHostLoggerEntryJson(std::to_string(skip + i),
1958 logEntries[i], hostLogEntry);
Spencer Kub7028eb2021-10-26 15:27:35 +08001959 }
1960
George Liu0fda0f12021-11-16 10:06:17 +08001961 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1962 if (skip + top < logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001963 {
George Liu0fda0f12021-11-16 10:06:17 +08001964 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1965 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
1966 std::to_string(skip + top);
Spencer Kub7028eb2021-10-26 15:27:35 +08001967 }
George Liu0fda0f12021-11-16 10:06:17 +08001968 }
1969 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001970}
1971
1972inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1973{
1974 BMCWEB_ROUTE(
1975 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1976 .privileges(redfish::privileges::getLogEntry)
1977 .methods(boost::beast::http::verb::get)(
Ed Tanousace85d62021-10-26 12:45:59 -07001978 [](const crow::Request& req,
Spencer Kub7028eb2021-10-26 15:27:35 +08001979 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1980 const std::string& param) {
1981 const std::string& targetID = param;
1982
1983 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001984
1985 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1986 const char* end = targetID.data() + targetID.size();
1987
1988 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
Spencer Kub7028eb2021-10-26 15:27:35 +08001989 if (ec == std::errc::invalid_argument)
1990 {
Ed Tanousace85d62021-10-26 12:45:59 -07001991 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
Spencer Kub7028eb2021-10-26 15:27:35 +08001992 return;
1993 }
1994 if (ec == std::errc::result_out_of_range)
1995 {
Ed Tanousace85d62021-10-26 12:45:59 -07001996 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
Spencer Kub7028eb2021-10-26 15:27:35 +08001997 return;
1998 }
1999
2000 std::vector<std::filesystem::path> hostLoggerFiles;
2001 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2002 {
2003 BMCWEB_LOG_ERROR << "fail to get host log file path";
2004 return;
2005 }
2006
2007 size_t logCount = 0;
2008 uint64_t top = 1;
2009 std::vector<std::string> logEntries;
2010 // We can get specific entry by skip and top. For example, if we
2011 // want to get nth entry, we can set skip = n-1 and top = 1 to
2012 // get that entry
2013 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
2014 logEntries, logCount))
2015 {
2016 messages::internalError(asyncResp->res);
2017 return;
2018 }
2019
2020 if (!logEntries.empty())
2021 {
2022 fillHostLoggerEntryJson(targetID, logEntries[0],
2023 asyncResp->res.jsonValue);
2024 return;
2025 }
2026
2027 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07002028 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
Spencer Kub7028eb2021-10-26 15:27:35 +08002029 });
2030}
2031
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002032inline void requestRoutesBMCLogServiceCollection(App& app)
2033{
2034 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002035 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002036 .methods(boost::beast::http::verb::get)(
2037 [](const crow::Request&,
2038 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2039 // Collections don't include the static data added by SubRoute
2040 // because it has a duplicate entry for members
2041 asyncResp->res.jsonValue["@odata.type"] =
2042 "#LogServiceCollection.LogServiceCollection";
2043 asyncResp->res.jsonValue["@odata.id"] =
2044 "/redfish/v1/Managers/bmc/LogServices";
2045 asyncResp->res.jsonValue["Name"] =
2046 "Open BMC Log Services Collection";
2047 asyncResp->res.jsonValue["Description"] =
2048 "Collection of LogServices for this Manager";
2049 nlohmann::json& logServiceArray =
2050 asyncResp->res.jsonValue["Members"];
2051 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002052#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002053 logServiceArray.push_back(
2054 {{"@odata.id",
2055 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002056#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002057#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002058 logServiceArray.push_back(
2059 {{"@odata.id",
2060 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002061#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002062 asyncResp->res.jsonValue["Members@odata.count"] =
2063 logServiceArray.size();
2064 });
2065}
Ed Tanous1da66f72018-07-27 16:13:37 -07002066
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002067inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002068{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002069 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002070 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002071 .methods(boost::beast::http::verb::get)(
2072 [](const crow::Request&,
2073 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002074
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002075 {
2076 asyncResp->res.jsonValue["@odata.type"] =
2077 "#LogService.v1_1_0.LogService";
2078 asyncResp->res.jsonValue["@odata.id"] =
2079 "/redfish/v1/Managers/bmc/LogServices/Journal";
2080 asyncResp->res.jsonValue["Name"] =
2081 "Open BMC Journal Log Service";
2082 asyncResp->res.jsonValue["Description"] =
2083 "BMC Journal Log Service";
2084 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2085 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302086
2087 std::pair<std::string, std::string> redfishDateTimeOffset =
2088 crow::utility::getDateTimeOffsetNow();
2089 asyncResp->res.jsonValue["DateTime"] =
2090 redfishDateTimeOffset.first;
2091 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2092 redfishDateTimeOffset.second;
2093
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002094 asyncResp->res.jsonValue["Entries"] = {
2095 {"@odata.id",
2096 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
2097 });
2098}
Jason M. Billse1f26342018-07-18 12:12:00 -07002099
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002100static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2101 sd_journal* journal,
2102 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002103{
2104 // Get the Log Entry contents
2105 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002106
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002107 std::string message;
2108 std::string_view syslogID;
2109 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2110 if (ret < 0)
2111 {
2112 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2113 << strerror(-ret);
2114 }
2115 if (!syslogID.empty())
2116 {
2117 message += std::string(syslogID) + ": ";
2118 }
2119
Ed Tanous39e77502019-03-04 17:35:53 -08002120 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002121 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002122 if (ret < 0)
2123 {
2124 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2125 return 1;
2126 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002127 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002128
2129 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002130 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002131 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002132 if (ret < 0)
2133 {
2134 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002135 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002136
2137 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002138 std::string entryTimeStr;
2139 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002140 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002141 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002142 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002143
2144 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002145 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002146 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002147 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2148 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002149 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002150 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002151 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002152 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002153 {"Severity", severity <= 2 ? "Critical"
2154 : severity <= 4 ? "Warning"
2155 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002156 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002157 {"Created", std::move(entryTimeStr)}};
2158 return 0;
2159}
2160
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002161inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002162{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002163 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002164 .privileges(redfish::privileges::getLogEntryCollection)
George Liu0fda0f12021-11-16 10:06:17 +08002165 .methods(
2166 boost::beast::http::verb::
2167 get)([](const crow::Request& req,
2168 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2169 static constexpr const long maxEntriesPerPage = 1000;
2170 uint64_t skip = 0;
2171 uint64_t top = maxEntriesPerPage; // Show max entries by default
2172 if (!getSkipParam(asyncResp, req, skip))
2173 {
2174 return;
2175 }
2176 if (!getTopParam(asyncResp, req, top))
2177 {
2178 return;
2179 }
2180 // Collections don't include the static data added by SubRoute
2181 // because it has a duplicate entry for members
2182 asyncResp->res.jsonValue["@odata.type"] =
2183 "#LogEntryCollection.LogEntryCollection";
2184 asyncResp->res.jsonValue["@odata.id"] =
2185 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2186 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2187 asyncResp->res.jsonValue["Description"] =
2188 "Collection of BMC Journal Entries";
2189 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2190 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002191
George Liu0fda0f12021-11-16 10:06:17 +08002192 // Go through the journal and use the timestamp to create a
2193 // unique ID for each entry
2194 sd_journal* journalTmp = nullptr;
2195 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2196 if (ret < 0)
2197 {
2198 BMCWEB_LOG_ERROR << "failed to open journal: "
2199 << strerror(-ret);
2200 messages::internalError(asyncResp->res);
2201 return;
2202 }
2203 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2204 journalTmp, sd_journal_close);
2205 journalTmp = nullptr;
2206 uint64_t entryCount = 0;
2207 // Reset the unique ID on the first entry
2208 bool firstEntry = true;
2209 SD_JOURNAL_FOREACH(journal.get())
2210 {
2211 entryCount++;
2212 // Handle paging using skip (number of entries to skip from
2213 // the start) and top (number of entries to display)
2214 if (entryCount <= skip || entryCount > skip + top)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002215 {
George Liu0fda0f12021-11-16 10:06:17 +08002216 continue;
2217 }
2218
2219 std::string idStr;
2220 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2221 {
2222 continue;
2223 }
2224
2225 if (firstEntry)
2226 {
2227 firstEntry = false;
2228 }
2229
2230 logEntryArray.push_back({});
2231 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2232 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2233 bmcJournalLogEntry) != 0)
2234 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002235 messages::internalError(asyncResp->res);
2236 return;
2237 }
George Liu0fda0f12021-11-16 10:06:17 +08002238 }
2239 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2240 if (skip + top < entryCount)
2241 {
2242 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2243 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2244 std::to_string(skip + top);
2245 }
2246 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002247}
Jason M. Billse1f26342018-07-18 12:12:00 -07002248
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002249inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002250{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002251 BMCWEB_ROUTE(app,
2252 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002253 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002254 .methods(boost::beast::http::verb::get)(
Ed Tanousace85d62021-10-26 12:45:59 -07002255 [](const crow::Request& req,
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002256 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2257 const std::string& entryID) {
2258 // Convert the unique ID back to a timestamp to find the entry
2259 uint64_t ts = 0;
2260 uint64_t index = 0;
2261 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2262 {
2263 return;
2264 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002265
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002266 sd_journal* journalTmp = nullptr;
2267 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2268 if (ret < 0)
2269 {
2270 BMCWEB_LOG_ERROR << "failed to open journal: "
2271 << strerror(-ret);
2272 messages::internalError(asyncResp->res);
2273 return;
2274 }
2275 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2276 journal(journalTmp, sd_journal_close);
2277 journalTmp = nullptr;
2278 // Go to the timestamp in the log and move to the entry at the
2279 // index tracking the unique ID
2280 std::string idStr;
2281 bool firstEntry = true;
2282 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2283 if (ret < 0)
2284 {
2285 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2286 << strerror(-ret);
2287 messages::internalError(asyncResp->res);
2288 return;
2289 }
2290 for (uint64_t i = 0; i <= index; i++)
2291 {
2292 sd_journal_next(journal.get());
2293 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2294 {
2295 messages::internalError(asyncResp->res);
2296 return;
2297 }
2298 if (firstEntry)
2299 {
2300 firstEntry = false;
2301 }
2302 }
2303 // Confirm that the entry ID matches what was requested
2304 if (idStr != entryID)
2305 {
Ed Tanousace85d62021-10-26 12:45:59 -07002306 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002307 return;
2308 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002309
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002310 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2311 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002312 {
2313 messages::internalError(asyncResp->res);
2314 return;
2315 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002316 });
2317}
2318
2319inline void requestRoutesBMCDumpService(App& app)
2320{
2321 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002322 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08002323 .methods(
2324 boost::beast::http::verb::
2325 get)([](const crow::Request&,
2326 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2327 asyncResp->res.jsonValue["@odata.id"] =
2328 "/redfish/v1/Managers/bmc/LogServices/Dump";
2329 asyncResp->res.jsonValue["@odata.type"] =
2330 "#LogService.v1_2_0.LogService";
2331 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2332 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2333 asyncResp->res.jsonValue["Id"] = "Dump";
2334 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302335
George Liu0fda0f12021-11-16 10:06:17 +08002336 std::pair<std::string, std::string> redfishDateTimeOffset =
2337 crow::utility::getDateTimeOffsetNow();
2338 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2339 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2340 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302341
George Liu0fda0f12021-11-16 10:06:17 +08002342 asyncResp->res.jsonValue["Entries"] = {
2343 {"@odata.id",
2344 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2345 asyncResp->res.jsonValue["Actions"] = {
2346 {"#LogService.ClearLog",
2347 {{"target",
2348 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog"}}},
2349 {"#LogService.CollectDiagnosticData",
2350 {{"target",
2351 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
2352 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002353}
2354
2355inline void requestRoutesBMCDumpEntryCollection(App& app)
2356{
2357
2358 /**
2359 * Functions triggers appropriate requests on DBus
2360 */
2361 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002362 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002363 .methods(boost::beast::http::verb::get)(
2364 [](const crow::Request&,
2365 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2366 asyncResp->res.jsonValue["@odata.type"] =
2367 "#LogEntryCollection.LogEntryCollection";
2368 asyncResp->res.jsonValue["@odata.id"] =
2369 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2370 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2371 asyncResp->res.jsonValue["Description"] =
2372 "Collection of BMC Dump Entries";
2373
2374 getDumpEntryCollection(asyncResp, "BMC");
2375 });
2376}
2377
2378inline void requestRoutesBMCDumpEntry(App& app)
2379{
2380 BMCWEB_ROUTE(app,
2381 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002382 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002383 .methods(boost::beast::http::verb::get)(
2384 [](const crow::Request&,
2385 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2386 const std::string& param) {
2387 getDumpEntryById(asyncResp, param, "BMC");
2388 });
2389 BMCWEB_ROUTE(app,
2390 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002391 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002392 .methods(boost::beast::http::verb::delete_)(
2393 [](const crow::Request&,
2394 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2395 const std::string& param) {
2396 deleteDumpEntry(asyncResp, param, "bmc");
2397 });
2398}
2399
2400inline void requestRoutesBMCDumpCreate(App& app)
2401{
2402
George Liu0fda0f12021-11-16 10:06:17 +08002403 BMCWEB_ROUTE(
2404 app,
2405 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002406 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002407 .methods(boost::beast::http::verb::post)(
2408 [](const crow::Request& req,
2409 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2410 createDump(asyncResp, req, "BMC");
2411 });
2412}
2413
2414inline void requestRoutesBMCDumpClear(App& app)
2415{
George Liu0fda0f12021-11-16 10:06:17 +08002416 BMCWEB_ROUTE(
2417 app,
2418 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002419 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002420 .methods(boost::beast::http::verb::post)(
2421 [](const crow::Request&,
2422 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2423 clearDump(asyncResp, "BMC");
2424 });
2425}
2426
2427inline void requestRoutesSystemDumpService(App& app)
2428{
2429 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002430 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002431 .methods(boost::beast::http::verb::get)(
2432 [](const crow::Request&,
2433 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2434
2435 {
2436 asyncResp->res.jsonValue["@odata.id"] =
2437 "/redfish/v1/Systems/system/LogServices/Dump";
2438 asyncResp->res.jsonValue["@odata.type"] =
2439 "#LogService.v1_2_0.LogService";
2440 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2441 asyncResp->res.jsonValue["Description"] =
2442 "System Dump LogService";
2443 asyncResp->res.jsonValue["Id"] = "Dump";
2444 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302445
2446 std::pair<std::string, std::string> redfishDateTimeOffset =
2447 crow::utility::getDateTimeOffsetNow();
2448 asyncResp->res.jsonValue["DateTime"] =
2449 redfishDateTimeOffset.first;
2450 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2451 redfishDateTimeOffset.second;
2452
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002453 asyncResp->res.jsonValue["Entries"] = {
2454 {"@odata.id",
2455 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2456 asyncResp->res.jsonValue["Actions"] = {
2457 {"#LogService.ClearLog",
2458 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002459 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002460 {"#LogService.CollectDiagnosticData",
2461 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002462 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002463 });
2464}
2465
2466inline void requestRoutesSystemDumpEntryCollection(App& app)
2467{
2468
2469 /**
2470 * Functions triggers appropriate requests on DBus
2471 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002472 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002473 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002474 .methods(boost::beast::http::verb::get)(
2475 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002476 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002477 asyncResp->res.jsonValue["@odata.type"] =
2478 "#LogEntryCollection.LogEntryCollection";
2479 asyncResp->res.jsonValue["@odata.id"] =
2480 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2481 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2482 asyncResp->res.jsonValue["Description"] =
2483 "Collection of System Dump Entries";
2484
2485 getDumpEntryCollection(asyncResp, "System");
2486 });
2487}
2488
2489inline void requestRoutesSystemDumpEntry(App& app)
2490{
2491 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002492 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002493 .privileges(redfish::privileges::getLogEntry)
2494
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002495 .methods(boost::beast::http::verb::get)(
2496 [](const crow::Request&,
2497 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2498 const std::string& param) {
2499 getDumpEntryById(asyncResp, param, "System");
2500 });
2501
2502 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002503 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002504 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002505 .methods(boost::beast::http::verb::delete_)(
2506 [](const crow::Request&,
2507 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2508 const std::string& param) {
2509 deleteDumpEntry(asyncResp, param, "system");
2510 });
2511}
2512
2513inline void requestRoutesSystemDumpCreate(App& app)
2514{
George Liu0fda0f12021-11-16 10:06:17 +08002515 BMCWEB_ROUTE(
2516 app,
2517 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002518 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002519 .methods(boost::beast::http::verb::post)(
2520 [](const crow::Request& req,
2521 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2522
2523 { createDump(asyncResp, req, "System"); });
2524}
2525
2526inline void requestRoutesSystemDumpClear(App& app)
2527{
George Liu0fda0f12021-11-16 10:06:17 +08002528 BMCWEB_ROUTE(
2529 app,
2530 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002531 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002532 .methods(boost::beast::http::verb::post)(
2533 [](const crow::Request&,
2534 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2535
2536 { clearDump(asyncResp, "System"); });
2537}
2538
2539inline void requestRoutesCrashdumpService(App& app)
2540{
2541 // Note: Deviated from redfish privilege registry for GET & HEAD
2542 // method for security reasons.
2543 /**
2544 * Functions triggers appropriate requests on DBus
2545 */
2546 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002547 // This is incorrect, should be:
2548 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002549 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002550 .methods(
2551 boost::beast::http::verb::
2552 get)([](const crow::Request&,
2553 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2554 // Copy over the static data to include the entries added by
2555 // SubRoute
2556 asyncResp->res.jsonValue["@odata.id"] =
2557 "/redfish/v1/Systems/system/LogServices/Crashdump";
2558 asyncResp->res.jsonValue["@odata.type"] =
2559 "#LogService.v1_2_0.LogService";
2560 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2561 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2562 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2563 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2564 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302565
2566 std::pair<std::string, std::string> redfishDateTimeOffset =
2567 crow::utility::getDateTimeOffsetNow();
2568 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2569 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2570 redfishDateTimeOffset.second;
2571
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002572 asyncResp->res.jsonValue["Entries"] = {
2573 {"@odata.id",
2574 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2575 asyncResp->res.jsonValue["Actions"] = {
2576 {"#LogService.ClearLog",
George Liu0fda0f12021-11-16 10:06:17 +08002577 {{"target",
2578 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002579 {"#LogService.CollectDiagnosticData",
George Liu0fda0f12021-11-16 10:06:17 +08002580 {{"target",
2581 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002582 });
2583}
2584
2585void inline requestRoutesCrashdumpClear(App& app)
2586{
George Liu0fda0f12021-11-16 10:06:17 +08002587 BMCWEB_ROUTE(
2588 app,
2589 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002590 // This is incorrect, should be:
2591 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002592 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002593 .methods(boost::beast::http::verb::post)(
2594 [](const crow::Request&,
2595 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2596 crow::connections::systemBus->async_method_call(
2597 [asyncResp](const boost::system::error_code ec,
2598 const std::string&) {
2599 if (ec)
2600 {
2601 messages::internalError(asyncResp->res);
2602 return;
2603 }
2604 messages::success(asyncResp->res);
2605 },
2606 crashdumpObject, crashdumpPath, deleteAllInterface,
2607 "DeleteAll");
2608 });
2609}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002610
zhanghch058d1b46d2021-04-01 11:18:24 +08002611static void
2612 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2613 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002614{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002615 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002616 [asyncResp, logID,
2617 &logEntryJson](const boost::system::error_code ec,
2618 const dbus::utility::DBusPropertiesMap& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002619 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002620 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002621 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2622 if (ec.value() ==
2623 boost::system::linux_error::bad_request_descriptor)
2624 {
2625 messages::resourceNotFound(asyncResp->res, "LogEntry",
2626 logID);
2627 }
2628 else
2629 {
2630 messages::internalError(asyncResp->res);
2631 }
2632 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002633 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002634
Johnathan Mantey043a0532020-03-10 17:15:28 -07002635 std::string timestamp{};
2636 std::string filename{};
2637 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002638 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002639
2640 if (filename.empty() || timestamp.empty())
2641 {
Ed Tanousace85d62021-10-26 12:45:59 -07002642 messages::resourceMissingAtURI(
2643 asyncResp->res, crow::utility::urlFromPieces(logID));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002644 return;
2645 }
2646
2647 std::string crashdumpURI =
2648 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2649 logID + "/" + filename;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002650 nlohmann::json logEntry = {
Jason M. Bills4978b632022-02-22 14:17:43 -08002651 {"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
2652 {"@odata.id",
2653 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2654 logID},
2655 {"Name", "CPU Crashdump"},
2656 {"Id", logID},
2657 {"EntryType", "Oem"},
2658 {"AdditionalDataURI", std::move(crashdumpURI)},
2659 {"DiagnosticDataType", "OEM"},
2660 {"OEMDiagnosticDataType", "PECICrashdump"},
2661 {"Created", std::move(timestamp)}};
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002662
2663 // If logEntryJson references an array of LogEntry resources
2664 // ('Members' list), then push this as a new entry, otherwise set it
2665 // directly
2666 if (logEntryJson.is_array())
2667 {
2668 logEntryJson.push_back(logEntry);
2669 asyncResp->res.jsonValue["Members@odata.count"] =
2670 logEntryJson.size();
2671 }
2672 else
2673 {
2674 logEntryJson = logEntry;
2675 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002676 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002677 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002678 std::move(getStoredLogCallback), crashdumpObject,
2679 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002680 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002681}
2682
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002683inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002684{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002685 // Note: Deviated from redfish privilege registry for GET & HEAD
2686 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002687 /**
2688 * Functions triggers appropriate requests on DBus
2689 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002690 BMCWEB_ROUTE(app,
2691 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002692 // This is incorrect, should be.
2693 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002694 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002695 .methods(
2696 boost::beast::http::verb::
2697 get)([](const crow::Request&,
2698 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002699 crow::connections::systemBus->async_method_call(
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002700 [asyncResp](const boost::system::error_code ec,
2701 const std::vector<std::string>& resp) {
2702 if (ec)
2703 {
2704 if (ec.value() !=
2705 boost::system::errc::no_such_file_or_directory)
2706 {
2707 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2708 << ec.message();
2709 messages::internalError(asyncResp->res);
2710 return;
2711 }
2712 }
2713 asyncResp->res.jsonValue["@odata.type"] =
2714 "#LogEntryCollection.LogEntryCollection";
2715 asyncResp->res.jsonValue["@odata.id"] =
2716 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2717 asyncResp->res.jsonValue["Name"] =
2718 "Open BMC Crashdump Entries";
2719 asyncResp->res.jsonValue["Description"] =
2720 "Collection of Crashdump Entries";
2721 asyncResp->res.jsonValue["Members"] =
2722 nlohmann::json::array();
Brandon Kima2dd60a2022-03-14 11:42:34 -07002723 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002724
2725 for (const std::string& path : resp)
2726 {
2727 const sdbusplus::message::object_path objPath(path);
2728 // Get the log ID
2729 std::string logID = objPath.filename();
2730 if (logID.empty())
2731 {
2732 continue;
2733 }
2734 // Add the log entry to the array
2735 logCrashdumpEntry(asyncResp, logID,
2736 asyncResp->res.jsonValue["Members"]);
2737 }
2738 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002739 "xyz.openbmc_project.ObjectMapper",
2740 "/xyz/openbmc_project/object_mapper",
2741 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2742 std::array<const char*, 1>{crashdumpInterface});
2743 });
2744}
Ed Tanous1da66f72018-07-27 16:13:37 -07002745
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002746inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002747{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002748 // Note: Deviated from redfish privilege registry for GET & HEAD
2749 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002750
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002751 BMCWEB_ROUTE(
2752 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002753 // this is incorrect, should be
2754 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002755 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002756 .methods(boost::beast::http::verb::get)(
2757 [](const crow::Request&,
2758 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2759 const std::string& param) {
2760 const std::string& logID = param;
2761 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2762 });
2763}
Ed Tanous1da66f72018-07-27 16:13:37 -07002764
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002765inline void requestRoutesCrashdumpFile(App& app)
2766{
2767 // Note: Deviated from redfish privilege registry for GET & HEAD
2768 // method for security reasons.
2769 BMCWEB_ROUTE(
2770 app,
2771 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002772 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002773 .methods(boost::beast::http::verb::get)(
Ed Tanousace85d62021-10-26 12:45:59 -07002774 [](const crow::Request& req,
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002775 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2776 const std::string& logID, const std::string& fileName) {
2777 auto getStoredLogCallback =
Ed Tanousace85d62021-10-26 12:45:59 -07002778 [asyncResp, logID, fileName,
2779 url(boost::urls::url(req.urlView))](
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002780 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002781 const std::vector<std::pair<
2782 std::string, dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002783 resp) {
2784 if (ec)
2785 {
2786 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2787 << ec.message();
2788 messages::internalError(asyncResp->res);
2789 return;
2790 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002791
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002792 std::string dbusFilename{};
2793 std::string dbusTimestamp{};
2794 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002795
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002796 parseCrashdumpParameters(resp, dbusFilename,
2797 dbusTimestamp, dbusFilepath);
2798
2799 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2800 dbusFilepath.empty())
2801 {
Ed Tanousace85d62021-10-26 12:45:59 -07002802 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002803 return;
2804 }
2805
2806 // Verify the file name parameter is correct
2807 if (fileName != dbusFilename)
2808 {
Ed Tanousace85d62021-10-26 12:45:59 -07002809 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002810 return;
2811 }
2812
2813 if (!std::filesystem::exists(dbusFilepath))
2814 {
Ed Tanousace85d62021-10-26 12:45:59 -07002815 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002816 return;
2817 }
Jason M. Bills2d314912022-01-12 13:59:01 -08002818 std::ifstream ifs(dbusFilepath,
2819 std::ios::in | std::ios::binary);
2820 asyncResp->res.body() = std::string(
2821 std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002822
2823 // Configure this to be a file download when accessed
2824 // from a browser
2825 asyncResp->res.addHeader("Content-Disposition",
2826 "attachment");
2827 };
2828 crow::connections::systemBus->async_method_call(
2829 std::move(getStoredLogCallback), crashdumpObject,
2830 crashdumpPath + std::string("/") + logID,
2831 "org.freedesktop.DBus.Properties", "GetAll",
2832 crashdumpInterface);
2833 });
2834}
2835
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002836enum class OEMDiagnosticType
2837{
2838 onDemand,
2839 telemetry,
2840 invalid,
2841};
2842
Ed Tanousf7725d72022-03-07 12:46:00 -08002843inline OEMDiagnosticType
2844 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002845{
2846 if (oemDiagStr == "OnDemand")
2847 {
2848 return OEMDiagnosticType::onDemand;
2849 }
2850 if (oemDiagStr == "Telemetry")
2851 {
2852 return OEMDiagnosticType::telemetry;
2853 }
2854
2855 return OEMDiagnosticType::invalid;
2856}
2857
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002858inline void requestRoutesCrashdumpCollect(App& app)
2859{
2860 // Note: Deviated from redfish privilege registry for GET & HEAD
2861 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002862 BMCWEB_ROUTE(
2863 app,
2864 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002865 // The below is incorrect; Should be ConfigureManager
2866 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002867 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002868 .methods(
2869 boost::beast::http::verb::
2870 post)([](const crow::Request& req,
2871 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2872 std::string diagnosticDataType;
2873 std::string oemDiagnosticDataType;
Willy Tu15ed6782021-12-14 11:03:16 -08002874 if (!redfish::json_util::readJsonAction(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002875 req, asyncResp->res, "DiagnosticDataType",
2876 diagnosticDataType, "OEMDiagnosticDataType",
2877 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002878 {
James Feist46229572020-02-19 15:11:58 -08002879 return;
2880 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002881
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002882 if (diagnosticDataType != "OEM")
2883 {
2884 BMCWEB_LOG_ERROR
2885 << "Only OEM DiagnosticDataType supported for Crashdump";
2886 messages::actionParameterValueFormatError(
2887 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2888 "CollectDiagnosticData");
2889 return;
2890 }
2891
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002892 OEMDiagnosticType oemDiagType =
2893 getOEMDiagnosticType(oemDiagnosticDataType);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002894
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002895 std::string iface;
2896 std::string method;
2897 std::string taskMatchStr;
2898 if (oemDiagType == OEMDiagnosticType::onDemand)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002899 {
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002900 iface = crashdumpOnDemandInterface;
2901 method = "GenerateOnDemandLog";
2902 taskMatchStr = "type='signal',"
2903 "interface='org.freedesktop.DBus.Properties',"
2904 "member='PropertiesChanged',"
2905 "arg0namespace='com.intel.crashdump'";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002906 }
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002907 else if (oemDiagType == OEMDiagnosticType::telemetry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002908 {
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002909 iface = crashdumpTelemetryInterface;
2910 method = "GenerateTelemetryLog";
2911 taskMatchStr = "type='signal',"
2912 "interface='org.freedesktop.DBus.Properties',"
2913 "member='PropertiesChanged',"
2914 "arg0namespace='com.intel.crashdump'";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002915 }
2916 else
2917 {
2918 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2919 << oemDiagnosticDataType;
2920 messages::actionParameterValueFormatError(
2921 asyncResp->res, oemDiagnosticDataType,
2922 "OEMDiagnosticDataType", "CollectDiagnosticData");
2923 return;
2924 }
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002925
2926 auto collectCrashdumpCallback =
2927 [asyncResp, payload(task::Payload(req)),
2928 taskMatchStr](const boost::system::error_code ec,
2929 const std::string&) mutable {
2930 if (ec)
2931 {
2932 if (ec.value() ==
2933 boost::system::errc::operation_not_supported)
2934 {
2935 messages::resourceInStandby(asyncResp->res);
2936 }
2937 else if (ec.value() ==
2938 boost::system::errc::device_or_resource_busy)
2939 {
2940 messages::serviceTemporarilyUnavailable(
2941 asyncResp->res, "60");
2942 }
2943 else
2944 {
2945 messages::internalError(asyncResp->res);
2946 }
2947 return;
2948 }
2949 std::shared_ptr<task::TaskData> task =
2950 task::TaskData::createTask(
2951 [](boost::system::error_code err,
2952 sdbusplus::message::message&,
2953 const std::shared_ptr<task::TaskData>&
2954 taskData) {
2955 if (!err)
2956 {
2957 taskData->messages.emplace_back(
2958 messages::taskCompletedOK(
2959 std::to_string(taskData->index)));
2960 taskData->state = "Completed";
2961 }
2962 return task::completed;
2963 },
2964 taskMatchStr);
2965
2966 task->startTimer(std::chrono::minutes(5));
2967 task->populateResp(asyncResp->res);
2968 task->payload.emplace(std::move(payload));
2969 };
2970
2971 crow::connections::systemBus->async_method_call(
2972 std::move(collectCrashdumpCallback), crashdumpObject,
2973 crashdumpPath, iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002974 });
2975}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002976
Andrew Geisslercb92c032018-08-17 07:56:14 -07002977/**
2978 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2979 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002980inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002981{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002982 /**
2983 * Function handles POST method request.
2984 * The Clear Log actions does not require any parameter.The action deletes
2985 * all entries found in the Entries collection for this Log Service.
2986 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002987
George Liu0fda0f12021-11-16 10:06:17 +08002988 BMCWEB_ROUTE(
2989 app,
2990 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002991 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002992 .methods(boost::beast::http::verb::post)(
2993 [](const crow::Request&,
2994 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2995 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07002996
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002997 // Process response from Logging service.
2998 auto respHandler = [asyncResp](
2999 const boost::system::error_code ec) {
3000 BMCWEB_LOG_DEBUG
3001 << "doClearLog resp_handler callback: Done";
3002 if (ec)
3003 {
3004 // TODO Handle for specific error code
3005 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
3006 << ec;
3007 asyncResp->res.result(
3008 boost::beast::http::status::internal_server_error);
3009 return;
3010 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003011
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003012 asyncResp->res.result(
3013 boost::beast::http::status::no_content);
3014 };
3015
3016 // Make call to Logging service to request Clear Log
3017 crow::connections::systemBus->async_method_call(
3018 respHandler, "xyz.openbmc_project.Logging",
3019 "/xyz/openbmc_project/logging",
3020 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3021 });
3022}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003023
3024/****************************************************
3025 * Redfish PostCode interfaces
3026 * using DBUS interface: getPostCodesTS
3027 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003028inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003029{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003030 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003031 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08003032 .methods(
3033 boost::beast::http::verb::
3034 get)([](const crow::Request&,
3035 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3036 asyncResp->res.jsonValue = {
3037 {"@odata.id",
3038 "/redfish/v1/Systems/system/LogServices/PostCodes"},
3039 {"@odata.type", "#LogService.v1_1_0.LogService"},
3040 {"Name", "POST Code Log Service"},
3041 {"Description", "POST Code Log Service"},
3042 {"Id", "BIOS POST Code Log"},
3043 {"OverWritePolicy", "WrapsWhenFull"},
3044 {"Entries",
3045 {{"@odata.id",
3046 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05303047
George Liu0fda0f12021-11-16 10:06:17 +08003048 std::pair<std::string, std::string> redfishDateTimeOffset =
3049 crow::utility::getDateTimeOffsetNow();
3050 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3051 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3052 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303053
George Liu0fda0f12021-11-16 10:06:17 +08003054 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3055 {"target",
3056 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3057 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003058}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003059
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003060inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003061{
George Liu0fda0f12021-11-16 10:06:17 +08003062 BMCWEB_ROUTE(
3063 app,
3064 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003065 // The following privilege is incorrect; It should be ConfigureManager
3066 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003067 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003068 .methods(boost::beast::http::verb::post)(
3069 [](const crow::Request&,
3070 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3071 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003072
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003073 // Make call to post-code service to request clear all
3074 crow::connections::systemBus->async_method_call(
3075 [asyncResp](const boost::system::error_code ec) {
3076 if (ec)
3077 {
3078 // TODO Handle for specific error code
3079 BMCWEB_LOG_ERROR
3080 << "doClearPostCodes resp_handler got error "
3081 << ec;
3082 asyncResp->res.result(boost::beast::http::status::
3083 internal_server_error);
3084 messages::internalError(asyncResp->res);
3085 return;
3086 }
3087 },
3088 "xyz.openbmc_project.State.Boot.PostCode0",
3089 "/xyz/openbmc_project/State/Boot/PostCode0",
3090 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3091 });
3092}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003093
3094static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003095 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303096 const boost::container::flat_map<
3097 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003098 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3099 const uint64_t skip = 0, const uint64_t top = 0)
3100{
3101 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003102 const registries::Message* message =
3103 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003104
3105 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003106 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003107
3108 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303109 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3110 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003111 {
3112 currentCodeIndex++;
3113 std::string postcodeEntryID =
3114 "B" + std::to_string(bootIndex) + "-" +
3115 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3116
3117 uint64_t usecSinceEpoch = code.first;
3118 uint64_t usTimeOffset = 0;
3119
3120 if (1 == currentCodeIndex)
3121 { // already incremented
3122 firstCodeTimeUs = code.first;
3123 }
3124 else
3125 {
3126 usTimeOffset = code.first - firstCodeTimeUs;
3127 }
3128
3129 // skip if no specific codeIndex is specified and currentCodeIndex does
3130 // not fall between top and skip
3131 if ((codeIndex == 0) &&
3132 (currentCodeIndex <= skip || currentCodeIndex > top))
3133 {
3134 continue;
3135 }
3136
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003137 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003138 // currentIndex
3139 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3140 {
3141 // This is done for simplicity. 1st entry is needed to calculate
3142 // time offset. To improve efficiency, one can get to the entry
3143 // directly (possibly with flatmap's nth method)
3144 continue;
3145 }
3146
3147 // currentCodeIndex is within top and skip or equal to specified code
3148 // index
3149
3150 // Get the Created time from the timestamp
3151 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003152 entryTimeStr =
3153 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003154
3155 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3156 std::ostringstream hexCode;
3157 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303158 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003159 std::ostringstream timeOffsetStr;
3160 // Set Fixed -Point Notation
3161 timeOffsetStr << std::fixed;
3162 // Set precision to 4 digits
3163 timeOffsetStr << std::setprecision(4);
3164 // Add double to stream
3165 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3166 std::vector<std::string> messageArgs = {
3167 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3168
3169 // Get MessageArgs template from message registry
3170 std::string msg;
3171 if (message != nullptr)
3172 {
3173 msg = message->message;
3174
3175 // fill in this post code value
3176 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003177 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003178 {
3179 std::string argStr = "%" + std::to_string(++i);
3180 size_t argPos = msg.find(argStr);
3181 if (argPos != std::string::npos)
3182 {
3183 msg.replace(argPos, argStr.length(), messageArg);
3184 }
3185 }
3186 }
3187
Tim Leed4342a92020-04-27 11:47:58 +08003188 // Get Severity template from message registry
3189 std::string severity;
3190 if (message != nullptr)
3191 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003192 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003193 }
3194
ZhikuiRena3316fc2020-01-29 14:58:08 -08003195 // add to AsyncResp
3196 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003197 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003198 bmcLogEntry = {
3199 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3200 {"@odata.id",
3201 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3202 postcodeEntryID},
3203 {"Name", "POST Code Log Entry"},
3204 {"Id", postcodeEntryID},
3205 {"Message", std::move(msg)},
3206 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3207 {"MessageArgs", std::move(messageArgs)},
3208 {"EntryType", "Event"},
3209 {"Severity", std::move(severity)},
3210 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003211 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3212 {
3213 bmcLogEntry["AdditionalDataURI"] =
3214 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3215 postcodeEntryID + "/attachment";
3216 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003217 }
3218}
3219
zhanghch058d1b46d2021-04-01 11:18:24 +08003220static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003221 const uint16_t bootIndex,
3222 const uint64_t codeIndex)
3223{
3224 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303225 [aResp, bootIndex,
3226 codeIndex](const boost::system::error_code ec,
3227 const boost::container::flat_map<
3228 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3229 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003230 if (ec)
3231 {
3232 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3233 messages::internalError(aResp->res);
3234 return;
3235 }
3236
3237 // skip the empty postcode boots
3238 if (postcode.empty())
3239 {
3240 return;
3241 }
3242
3243 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3244
3245 aResp->res.jsonValue["Members@odata.count"] =
3246 aResp->res.jsonValue["Members"].size();
3247 },
Jonathan Doman15124762021-01-07 17:54:17 -08003248 "xyz.openbmc_project.State.Boot.PostCode0",
3249 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003250 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3251 bootIndex);
3252}
3253
zhanghch058d1b46d2021-04-01 11:18:24 +08003254static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003255 const uint16_t bootIndex,
3256 const uint16_t bootCount,
3257 const uint64_t entryCount, const uint64_t skip,
3258 const uint64_t top)
3259{
3260 crow::connections::systemBus->async_method_call(
3261 [aResp, bootIndex, bootCount, entryCount, skip,
3262 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303263 const boost::container::flat_map<
3264 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3265 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003266 if (ec)
3267 {
3268 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3269 messages::internalError(aResp->res);
3270 return;
3271 }
3272
3273 uint64_t endCount = entryCount;
3274 if (!postcode.empty())
3275 {
3276 endCount = entryCount + postcode.size();
3277
3278 if ((skip < endCount) && ((top + skip) > entryCount))
3279 {
3280 uint64_t thisBootSkip =
3281 std::max(skip, entryCount) - entryCount;
3282 uint64_t thisBootTop =
3283 std::min(top + skip, endCount) - entryCount;
3284
3285 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3286 thisBootSkip, thisBootTop);
3287 }
3288 aResp->res.jsonValue["Members@odata.count"] = endCount;
3289 }
3290
3291 // continue to previous bootIndex
3292 if (bootIndex < bootCount)
3293 {
3294 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3295 bootCount, endCount, skip, top);
3296 }
3297 else
3298 {
3299 aResp->res.jsonValue["Members@odata.nextLink"] =
George Liu0fda0f12021-11-16 10:06:17 +08003300 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
ZhikuiRena3316fc2020-01-29 14:58:08 -08003301 std::to_string(skip + top);
3302 }
3303 },
Jonathan Doman15124762021-01-07 17:54:17 -08003304 "xyz.openbmc_project.State.Boot.PostCode0",
3305 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003306 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3307 bootIndex);
3308}
3309
zhanghch058d1b46d2021-04-01 11:18:24 +08003310static void
3311 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3312 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003313{
3314 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003315 sdbusplus::asio::getProperty<uint16_t>(
3316 *crow::connections::systemBus,
3317 "xyz.openbmc_project.State.Boot.PostCode0",
3318 "/xyz/openbmc_project/State/Boot/PostCode0",
3319 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3320 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3321 const uint16_t bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003322 if (ec)
3323 {
3324 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3325 messages::internalError(aResp->res);
3326 return;
3327 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003328 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3329 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003330}
3331
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003332inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003333{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003334 BMCWEB_ROUTE(app,
3335 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003336 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003337 .methods(boost::beast::http::verb::get)(
3338 [](const crow::Request& req,
3339 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3340 asyncResp->res.jsonValue["@odata.type"] =
3341 "#LogEntryCollection.LogEntryCollection";
3342 asyncResp->res.jsonValue["@odata.id"] =
3343 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3344 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3345 asyncResp->res.jsonValue["Description"] =
3346 "Collection of POST Code Log Entries";
3347 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3348 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003349
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003350 uint64_t skip = 0;
3351 uint64_t top = maxEntriesPerPage; // Show max entries by default
3352 if (!getSkipParam(asyncResp, req, skip))
3353 {
3354 return;
3355 }
3356 if (!getTopParam(asyncResp, req, top))
3357 {
3358 return;
3359 }
3360 getCurrentBootNumber(asyncResp, skip, top);
3361 });
3362}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003363
George Liu647b3cd2021-07-05 12:43:56 +08003364/**
3365 * @brief Parse post code ID and get the current value and index value
3366 * eg: postCodeID=B1-2, currentValue=1, index=2
3367 *
3368 * @param[in] postCodeID Post Code ID
3369 * @param[out] currentValue Current value
3370 * @param[out] index Index value
3371 *
3372 * @return bool true if the parsing is successful, false the parsing fails
3373 */
3374inline static bool parsePostCode(const std::string& postCodeID,
3375 uint64_t& currentValue, uint16_t& index)
3376{
3377 std::vector<std::string> split;
3378 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3379 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3380 {
3381 return false;
3382 }
3383
Ed Tanousca45aa32022-01-07 09:28:45 -08003384 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003385 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003386 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003387 const char* end = split[0].data() + split[0].size();
3388 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3389
3390 if (ptrIndex != end || ecIndex != std::errc())
3391 {
3392 return false;
3393 }
3394
3395 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003396
3397 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003398 end = split[1].data() + split[1].size();
3399 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003400
Ed Tanousdcf2ebc2022-01-25 10:07:45 -08003401 return ptrValue == end && ecValue != std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003402}
3403
3404inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3405{
George Liu0fda0f12021-11-16 10:06:17 +08003406 BMCWEB_ROUTE(
3407 app,
3408 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003409 .privileges(redfish::privileges::getLogEntry)
3410 .methods(boost::beast::http::verb::get)(
3411 [](const crow::Request& req,
3412 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3413 const std::string& postCodeID) {
3414 if (!http_helpers::isOctetAccepted(
3415 req.getHeaderValue("Accept")))
3416 {
3417 asyncResp->res.result(
3418 boost::beast::http::status::bad_request);
3419 return;
3420 }
3421
3422 uint64_t currentValue = 0;
3423 uint16_t index = 0;
3424 if (!parsePostCode(postCodeID, currentValue, index))
3425 {
3426 messages::resourceNotFound(asyncResp->res, "LogEntry",
3427 postCodeID);
3428 return;
3429 }
3430
3431 crow::connections::systemBus->async_method_call(
3432 [asyncResp, postCodeID, currentValue](
3433 const boost::system::error_code ec,
3434 const std::vector<std::tuple<
3435 uint64_t, std::vector<uint8_t>>>& postcodes) {
3436 if (ec.value() == EBADR)
3437 {
3438 messages::resourceNotFound(asyncResp->res,
3439 "LogEntry", postCodeID);
3440 return;
3441 }
3442 if (ec)
3443 {
3444 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3445 messages::internalError(asyncResp->res);
3446 return;
3447 }
3448
3449 size_t value = static_cast<size_t>(currentValue) - 1;
3450 if (value == std::string::npos ||
3451 postcodes.size() < currentValue)
3452 {
3453 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3454 messages::resourceNotFound(asyncResp->res,
3455 "LogEntry", postCodeID);
3456 return;
3457 }
3458
Ed Tanous9eb808c2022-01-25 10:19:23 -08003459 const auto& [tID, c] = postcodes[value];
Ed Tanous46ff87b2022-01-07 09:25:51 -08003460 if (c.empty())
George Liu647b3cd2021-07-05 12:43:56 +08003461 {
3462 BMCWEB_LOG_INFO << "No found post code data";
3463 messages::resourceNotFound(asyncResp->res,
3464 "LogEntry", postCodeID);
3465 return;
3466 }
Ed Tanous46ff87b2022-01-07 09:25:51 -08003467 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3468 const char* d = reinterpret_cast<const char*>(c.data());
3469 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003470
3471 asyncResp->res.addHeader("Content-Type",
3472 "application/octet-stream");
3473 asyncResp->res.addHeader("Content-Transfer-Encoding",
3474 "Base64");
3475 asyncResp->res.body() =
3476 crow::utility::base64encode(strData);
3477 },
3478 "xyz.openbmc_project.State.Boot.PostCode0",
3479 "/xyz/openbmc_project/State/Boot/PostCode0",
3480 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3481 index);
3482 });
3483}
3484
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003485inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003486{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003487 BMCWEB_ROUTE(
3488 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003489 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003490 .methods(boost::beast::http::verb::get)(
Ed Tanousace85d62021-10-26 12:45:59 -07003491 [](const crow::Request& req,
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003492 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3493 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003494 uint16_t bootIndex = 0;
3495 uint64_t codeIndex = 0;
3496 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003497 {
3498 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07003499 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003500 return;
3501 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003502 if (bootIndex == 0 || codeIndex == 0)
3503 {
3504 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3505 << targetID;
3506 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003507
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003508 asyncResp->res.jsonValue["@odata.type"] =
3509 "#LogEntry.v1_4_0.LogEntry";
3510 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08003511 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003512 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3513 asyncResp->res.jsonValue["Description"] =
3514 "Collection of POST Code Log Entries";
3515 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3516 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003517
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003518 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3519 });
3520}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003521
Ed Tanous1da66f72018-07-27 16:13:37 -07003522} // namespace redfish