blob: 0a78009b95896baf976c1e63114cd3242a43924e [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 Tanous45ca1b82022-03-25 13:07:27 -070037#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070038#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039
George Liu647b3cd2021-07-05 12:43:56 +080040#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070041#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080042#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070043#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070044#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080045#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070046
47namespace redfish
48{
49
Gunnar Mills1214b7e2020-06-04 10:11:30 -050050constexpr char const* crashdumpObject = "com.intel.crashdump";
51constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050052constexpr char const* crashdumpInterface = "com.intel.crashdump";
53constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070054 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050055constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070056 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070057constexpr char const* crashdumpTelemetryInterface =
58 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070059
Ed Tanousfffb8c12022-02-07 23:53:03 -080060namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -070061{
Ed Tanous26702d02021-11-03 15:02:33 -070062static const Message*
63 getMessageFromRegistry(const std::string& messageKey,
64 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070065{
Ed Tanous26702d02021-11-03 15:02:33 -070066 std::span<const MessageEntry>::iterator messageIt = std::find_if(
67 registry.begin(), registry.end(),
68 [&messageKey](const MessageEntry& messageEntry) {
Ed Tanouse662eae2022-01-25 10:39:19 -080069 return std::strcmp(messageEntry.first, messageKey.c_str()) == 0;
Ed Tanous26702d02021-11-03 15:02:33 -070070 });
71 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070072 {
73 return &messageIt->second;
74 }
75
76 return nullptr;
77}
78
Gunnar Mills1214b7e2020-06-04 10:11:30 -050079static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070080{
81 // Redfish MessageIds are in the form
82 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
83 // the right Message
84 std::vector<std::string> fields;
85 fields.reserve(4);
86 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050087 std::string& registryName = fields[0];
88 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070089
90 // Find the right registry and check it for the MessageKey
91 if (std::string(base::header.registryPrefix) == registryName)
92 {
93 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070094 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070095 }
96 if (std::string(openbmc::header.registryPrefix) == registryName)
97 {
98 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070099 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -0700100 }
101 return nullptr;
102}
Ed Tanousfffb8c12022-02-07 23:53:03 -0800103} // namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -0700104
James Feistf6150402019-01-08 10:36:20 -0800105namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700106
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500107inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700108{
Ed Tanousd4d25792020-09-29 15:15:03 -0700109 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
110 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
111 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
112 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700113 {
114 return "Critical";
115 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700116 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
117 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
118 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700119 {
120 return "OK";
121 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700122 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700123 {
124 return "Warning";
125 }
126 return "";
127}
128
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700129inline static int getJournalMetadata(sd_journal* journal,
130 const std::string_view& field,
131 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700132{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500133 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700134 size_t length = 0;
135 int ret = 0;
136 // Get the metadata from the requested field of the journal entry
Ed Tanous46ff87b2022-01-07 09:25:51 -0800137 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
138 const void** dataVoid = reinterpret_cast<const void**>(&data);
139
140 ret = sd_journal_get_data(journal, field.data(), dataVoid, &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700141 if (ret < 0)
142 {
143 return ret;
144 }
Ed Tanous39e77502019-03-04 17:35:53 -0800145 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700146 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000147 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700148 return ret;
149}
150
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700151inline static int getJournalMetadata(sd_journal* journal,
152 const std::string_view& field,
153 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700154{
155 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800156 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700157 // Get the metadata from the requested field of the journal entry
158 ret = getJournalMetadata(journal, field, metadata);
159 if (ret < 0)
160 {
161 return ret;
162 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000163 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700164 return ret;
165}
166
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700167inline static bool getEntryTimestamp(sd_journal* journal,
168 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800169{
170 int ret = 0;
171 uint64_t timestamp = 0;
172 ret = sd_journal_get_realtime_usec(journal, &timestamp);
173 if (ret < 0)
174 {
175 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
176 << strerror(-ret);
177 return false;
178 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800179 entryTimestamp = crow::utility::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500180 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800181}
Ed Tanous50b8a432022-02-03 16:29:50 -0800182
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700183inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
184 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700185{
186 int ret = 0;
187 static uint64_t prevTs = 0;
188 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700189 if (firstEntry)
190 {
191 prevTs = 0;
192 }
193
Jason M. Bills16428a12018-11-02 12:42:29 -0700194 // Get the entry timestamp
195 uint64_t curTs = 0;
196 ret = sd_journal_get_realtime_usec(journal, &curTs);
197 if (ret < 0)
198 {
199 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
200 << strerror(-ret);
201 return false;
202 }
203 // If the timestamp isn't unique, increment the index
204 if (curTs == prevTs)
205 {
206 index++;
207 }
208 else
209 {
210 // Otherwise, reset it
211 index = 0;
212 }
213 // Save the timestamp
214 prevTs = curTs;
215
216 entryID = std::to_string(curTs);
217 if (index > 0)
218 {
219 entryID += "_" + std::to_string(index);
220 }
221 return true;
222}
223
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500224static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700225 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700226{
Ed Tanous271584a2019-07-09 16:24:22 -0700227 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700228 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700229 if (firstEntry)
230 {
231 prevTs = 0;
232 }
233
Jason M. Bills95820182019-04-22 16:25:34 -0700234 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700235 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700236 std::tm timeStruct = {};
237 std::istringstream entryStream(logEntry);
238 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
239 {
240 curTs = std::mktime(&timeStruct);
241 }
242 // If the timestamp isn't unique, increment the index
243 if (curTs == prevTs)
244 {
245 index++;
246 }
247 else
248 {
249 // Otherwise, reset it
250 index = 0;
251 }
252 // Save the timestamp
253 prevTs = curTs;
254
255 entryID = std::to_string(curTs);
256 if (index > 0)
257 {
258 entryID += "_" + std::to_string(index);
259 }
260 return true;
261}
262
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700263inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800264 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
265 const std::string& entryID, uint64_t& timestamp,
266 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700267{
268 if (entryID.empty())
269 {
270 return false;
271 }
272 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800273 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700274
Ed Tanous81ce6092020-12-17 16:54:55 +0000275 auto underscorePos = tsStr.find('_');
Ed Tanous71d5d8d2022-01-25 11:04:33 -0800276 if (underscorePos != std::string_view::npos)
Jason M. Bills16428a12018-11-02 12:42:29 -0700277 {
278 // Timestamp has an index
279 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800280 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700281 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700282 auto [ptr, ec] = std::from_chars(
283 indexStr.data(), indexStr.data() + indexStr.size(), index);
284 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700285 {
Ed Tanousace85d62021-10-26 12:45:59 -0700286 messages::resourceMissingAtURI(
287 asyncResp->res, crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700288 return false;
289 }
290 }
291 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700292 auto [ptr, ec] =
293 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
294 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700295 {
Ed Tanousace85d62021-10-26 12:45:59 -0700296 messages::resourceMissingAtURI(asyncResp->res,
297 crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700298 return false;
299 }
300 return true;
301}
302
Jason M. Bills95820182019-04-22 16:25:34 -0700303static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500304 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700305{
306 static const std::filesystem::path redfishLogDir = "/var/log";
307 static const std::string redfishLogFilename = "redfish";
308
309 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500310 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700311 std::filesystem::directory_iterator(redfishLogDir))
312 {
313 // If we find a redfish log file, save the path
314 std::string filename = dirEnt.path().filename();
315 if (boost::starts_with(filename, redfishLogFilename))
316 {
317 redfishLogFiles.emplace_back(redfishLogDir / filename);
318 }
319 }
320 // As the log files rotate, they are appended with a ".#" that is higher for
321 // the older logs. Since we don't expect more than 10 log files, we
322 // can just sort the list to get them in order from newest to oldest
323 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
324
325 return !redfishLogFiles.empty();
326}
327
zhanghch058d1b46d2021-04-01 11:18:24 +0800328inline void
329 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
330 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500331{
332 std::string dumpPath;
333 if (dumpType == "BMC")
334 {
335 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
336 }
337 else if (dumpType == "System")
338 {
339 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
340 }
341 else
342 {
343 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
344 messages::internalError(asyncResp->res);
345 return;
346 }
347
348 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800349 [asyncResp, dumpPath,
350 dumpType](const boost::system::error_code ec,
351 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500352 if (ec)
353 {
354 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
355 messages::internalError(asyncResp->res);
356 return;
357 }
358
359 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
360 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500361 std::string dumpEntryPath =
362 "/xyz/openbmc_project/dump/" +
363 std::string(boost::algorithm::to_lower_copy(dumpType)) +
364 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500365
Claire Weinan565dfb62022-04-27 23:05:12 -0700366 std::sort(resp.begin(), resp.end(),
367 [](const auto& l, const auto& r) {
368 return AlphanumLess<std::string>()(
369 l.first.filename(), r.first.filename());
370 });
371
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500372 for (auto& object : resp)
373 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500374 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500375 {
376 continue;
377 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800378 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500379 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500380 std::string dumpStatus;
381 nlohmann::json thisEntry;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000382
383 std::string entryID = object.first.filename();
384 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500385 {
386 continue;
387 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500388
389 for (auto& interfaceMap : object.second)
390 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500391 if (interfaceMap.first ==
392 "xyz.openbmc_project.Common.Progress")
393 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800394 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500395 {
396 if (propertyMap.first == "Status")
397 {
Ed Tanous55f79e62022-01-25 11:26:16 -0800398 const auto* status = std::get_if<std::string>(
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500399 &propertyMap.second);
400 if (status == nullptr)
401 {
402 messages::internalError(asyncResp->res);
403 break;
404 }
405 dumpStatus = *status;
406 }
407 }
408 }
409 else if (interfaceMap.first ==
410 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500411 {
412
413 for (auto& propertyMap : interfaceMap.second)
414 {
415 if (propertyMap.first == "Size")
416 {
Ed Tanous55f79e62022-01-25 11:26:16 -0800417 const auto* sizePtr =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500418 std::get_if<uint64_t>(&propertyMap.second);
419 if (sizePtr == nullptr)
420 {
421 messages::internalError(asyncResp->res);
422 break;
423 }
424 size = *sizePtr;
425 break;
426 }
427 }
428 }
429 else if (interfaceMap.first ==
430 "xyz.openbmc_project.Time.EpochTime")
431 {
432
Ed Tanous9eb808c2022-01-25 10:19:23 -0800433 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500434 {
435 if (propertyMap.first == "Elapsed")
436 {
437 const uint64_t* usecsTimeStamp =
438 std::get_if<uint64_t>(&propertyMap.second);
439 if (usecsTimeStamp == nullptr)
440 {
441 messages::internalError(asyncResp->res);
442 break;
443 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800444 timestamp = (*usecsTimeStamp / 1000 / 1000);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500445 break;
446 }
447 }
448 }
449 }
450
George Liu0fda0f12021-11-16 10:06:17 +0800451 if (dumpStatus !=
452 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500453 !dumpStatus.empty())
454 {
455 // Dump status is not Complete, no need to enumerate
456 continue;
457 }
458
George Liu647b3cd2021-07-05 12:43:56 +0800459 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500460 thisEntry["@odata.id"] = dumpPath + entryID;
461 thisEntry["Id"] = entryID;
462 thisEntry["EntryType"] = "Event";
Nan Zhou1d8782e2021-11-29 22:23:18 -0800463 thisEntry["Created"] =
464 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500465 thisEntry["Name"] = dumpType + " Dump Entry";
466
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500467 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500468
469 if (dumpType == "BMC")
470 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500471 thisEntry["DiagnosticDataType"] = "Manager";
472 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500473 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
474 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500475 }
476 else if (dumpType == "System")
477 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500478 thisEntry["DiagnosticDataType"] = "OEM";
479 thisEntry["OEMDiagnosticDataType"] = "System";
480 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500481 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
482 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500483 }
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500484 entriesArray.push_back(std::move(thisEntry));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500485 }
486 asyncResp->res.jsonValue["Members@odata.count"] =
487 entriesArray.size();
488 },
489 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
490 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
491}
492
zhanghch058d1b46d2021-04-01 11:18:24 +0800493inline void
Ed Tanous45ca1b82022-03-25 13:07:27 -0700494 getDumpEntryById(crow::App& app, const crow::Request& req,
495 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800496 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500497{
Ed Tanous45ca1b82022-03-25 13:07:27 -0700498 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
499 {
500 return;
501 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500502 std::string dumpPath;
503 if (dumpType == "BMC")
504 {
505 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
506 }
507 else if (dumpType == "System")
508 {
509 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
510 }
511 else
512 {
513 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
514 messages::internalError(asyncResp->res);
515 return;
516 }
517
518 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800519 [asyncResp, entryID, dumpPath,
520 dumpType](const boost::system::error_code ec,
521 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500522 if (ec)
523 {
524 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
525 messages::internalError(asyncResp->res);
526 return;
527 }
528
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500529 bool foundDumpEntry = false;
530 std::string dumpEntryPath =
531 "/xyz/openbmc_project/dump/" +
532 std::string(boost::algorithm::to_lower_copy(dumpType)) +
533 "/entry/";
534
Ed Tanous9eb808c2022-01-25 10:19:23 -0800535 for (const auto& objectPath : resp)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500536 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500537 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500538 {
539 continue;
540 }
541
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500542 foundDumpEntry = true;
Nan Zhou1d8782e2021-11-29 22:23:18 -0800543 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500544 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500545 std::string dumpStatus;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500546
Ed Tanous9eb808c2022-01-25 10:19:23 -0800547 for (const auto& interfaceMap : objectPath.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500548 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500549 if (interfaceMap.first ==
550 "xyz.openbmc_project.Common.Progress")
551 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800552 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500553 {
554 if (propertyMap.first == "Status")
555 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800556 const std::string* status =
557 std::get_if<std::string>(
558 &propertyMap.second);
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500559 if (status == nullptr)
560 {
561 messages::internalError(asyncResp->res);
562 break;
563 }
564 dumpStatus = *status;
565 }
566 }
567 }
568 else if (interfaceMap.first ==
569 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500570 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800571 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500572 {
573 if (propertyMap.first == "Size")
574 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800575 const uint64_t* sizePtr =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500576 std::get_if<uint64_t>(&propertyMap.second);
577 if (sizePtr == nullptr)
578 {
579 messages::internalError(asyncResp->res);
580 break;
581 }
582 size = *sizePtr;
583 break;
584 }
585 }
586 }
587 else if (interfaceMap.first ==
588 "xyz.openbmc_project.Time.EpochTime")
589 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800590 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500591 {
592 if (propertyMap.first == "Elapsed")
593 {
594 const uint64_t* usecsTimeStamp =
595 std::get_if<uint64_t>(&propertyMap.second);
596 if (usecsTimeStamp == nullptr)
597 {
598 messages::internalError(asyncResp->res);
599 break;
600 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800601 timestamp = *usecsTimeStamp / 1000 / 1000;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500602 break;
603 }
604 }
605 }
606 }
607
George Liu0fda0f12021-11-16 10:06:17 +0800608 if (dumpStatus !=
609 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500610 !dumpStatus.empty())
611 {
612 // Dump status is not Complete
613 // return not found until status is changed to Completed
614 messages::resourceNotFound(asyncResp->res,
615 dumpType + " dump", entryID);
616 return;
617 }
618
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500619 asyncResp->res.jsonValue["@odata.type"] =
George Liu647b3cd2021-07-05 12:43:56 +0800620 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500621 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
622 asyncResp->res.jsonValue["Id"] = entryID;
623 asyncResp->res.jsonValue["EntryType"] = "Event";
624 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800625 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500626 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
627
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500628 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500629
630 if (dumpType == "BMC")
631 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500632 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
633 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500634 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
635 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500636 }
637 else if (dumpType == "System")
638 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500639 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
640 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500641 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500642 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500643 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
644 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500645 }
646 }
Ed Tanouse05aec52022-01-25 10:28:56 -0800647 if (!foundDumpEntry)
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500648 {
649 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
650 messages::internalError(asyncResp->res);
651 return;
652 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500653 },
654 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
655 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
656}
657
zhanghch058d1b46d2021-04-01 11:18:24 +0800658inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800659 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500660 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500661{
George Liu3de8d8b2021-03-22 17:49:39 +0800662 auto respHandler = [asyncResp,
663 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500664 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
665 if (ec)
666 {
George Liu3de8d8b2021-03-22 17:49:39 +0800667 if (ec.value() == EBADR)
668 {
669 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
670 return;
671 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500672 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
673 << ec;
674 messages::internalError(asyncResp->res);
675 return;
676 }
677 };
678 crow::connections::systemBus->async_method_call(
679 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500680 "/xyz/openbmc_project/dump/" +
681 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
682 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500683 "xyz.openbmc_project.Object.Delete", "Delete");
684}
685
zhanghch058d1b46d2021-04-01 11:18:24 +0800686inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700687 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800688 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
689 const uint32_t& dumpId, const std::string& dumpPath,
690 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500691{
692 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500693 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500694 boost::system::error_code err, sdbusplus::message::message& m,
695 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000696 if (err)
697 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500698 BMCWEB_LOG_ERROR << "Error in creating a dump";
699 taskData->state = "Cancelled";
700 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000701 }
Ed Tanousb9d36b42022-02-26 21:42:46 -0800702
703 dbus::utility::DBusInteracesMap interfacesList;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500704
705 sdbusplus::message::object_path objPath;
706
707 m.read(objPath, interfacesList);
708
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500709 if (objPath.str ==
710 "/xyz/openbmc_project/dump/" +
711 std::string(boost::algorithm::to_lower_copy(dumpType)) +
712 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500713 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500714 nlohmann::json retMessage = messages::success();
715 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500716
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500717 std::string headerLoc =
718 "Location: " + dumpPath + std::to_string(dumpId);
719 taskData->payload->httpHeaders.emplace_back(
720 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500721
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500722 taskData->state = "Completed";
723 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500724 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500725 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500726 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800727 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500728 "member='InterfacesAdded', "
729 "path='/xyz/openbmc_project/dump'");
730
731 task->startTimer(std::chrono::minutes(3));
732 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700733 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500734}
735
zhanghch058d1b46d2021-04-01 11:18:24 +0800736inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
737 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500738{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500739 std::string dumpPath;
740 if (dumpType == "BMC")
741 {
742 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
743 }
744 else if (dumpType == "System")
745 {
746 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
747 }
748 else
749 {
750 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
751 messages::internalError(asyncResp->res);
752 return;
753 }
754
755 std::optional<std::string> diagnosticDataType;
756 std::optional<std::string> oemDiagnosticDataType;
757
Willy Tu15ed6782021-12-14 11:03:16 -0800758 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500759 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
760 "OEMDiagnosticDataType", oemDiagnosticDataType))
761 {
762 return;
763 }
764
765 if (dumpType == "System")
766 {
767 if (!oemDiagnosticDataType || !diagnosticDataType)
768 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800769 BMCWEB_LOG_ERROR
770 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500771 messages::actionParameterMissing(
772 asyncResp->res, "CollectDiagnosticData",
773 "DiagnosticDataType & OEMDiagnosticDataType");
774 return;
775 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700776 if ((*oemDiagnosticDataType != "System") ||
777 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500778 {
779 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700780 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500781 return;
782 }
783 }
784 else if (dumpType == "BMC")
785 {
786 if (!diagnosticDataType)
787 {
George Liu0fda0f12021-11-16 10:06:17 +0800788 BMCWEB_LOG_ERROR
789 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500790 messages::actionParameterMissing(
791 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
792 return;
793 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700794 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500795 {
796 BMCWEB_LOG_ERROR
797 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700798 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500799 return;
800 }
801 }
802
803 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700804 [asyncResp, payload(task::Payload(req)), dumpPath,
805 dumpType](const boost::system::error_code ec,
806 const uint32_t& dumpId) mutable {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500807 if (ec)
808 {
809 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
810 messages::internalError(asyncResp->res);
811 return;
812 }
813 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
814
Ed Tanous98be3e32021-09-16 15:05:36 -0700815 createDumpTaskCallback(std::move(payload), asyncResp, dumpId,
816 dumpPath, dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500817 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500818 "xyz.openbmc_project.Dump.Manager",
819 "/xyz/openbmc_project/dump/" +
820 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500821 "xyz.openbmc_project.Dump.Create", "CreateDump");
822}
823
zhanghch058d1b46d2021-04-01 11:18:24 +0800824inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
825 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500826{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500827 std::string dumpTypeLowerCopy =
828 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800829
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500830 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800831 [asyncResp, dumpType](
832 const boost::system::error_code ec,
833 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500834 if (ec)
835 {
836 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
837 messages::internalError(asyncResp->res);
838 return;
839 }
840
841 for (const std::string& path : subTreePaths)
842 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000843 sdbusplus::message::object_path objPath(path);
844 std::string logID = objPath.filename();
845 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500846 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000847 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500848 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000849 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500850 }
851 },
852 "xyz.openbmc_project.ObjectMapper",
853 "/xyz/openbmc_project/object_mapper",
854 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500855 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
856 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
857 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500858}
859
Ed Tanousb9d36b42022-02-26 21:42:46 -0800860inline static void
861 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
862 std::string& filename, std::string& timestamp,
863 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700864{
865 for (auto property : params)
866 {
867 if (property.first == "Timestamp")
868 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500869 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500870 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700871 if (value != nullptr)
872 {
873 timestamp = *value;
874 }
875 }
876 else if (property.first == "Filename")
877 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500878 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500879 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700880 if (value != nullptr)
881 {
882 filename = *value;
883 }
884 }
885 else if (property.first == "Log")
886 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500887 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500888 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700889 if (value != nullptr)
890 {
891 logfile = *value;
892 }
893 }
894 }
895}
896
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500897constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700898inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700899{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800900 /**
901 * Functions triggers appropriate requests on DBus
902 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700903 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700904 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700905 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
906 const std::shared_ptr<
907 bmcweb::AsyncResp>&
908 asyncResp) {
909 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700910 {
Ed Tanous45ca1b82022-03-25 13:07:27 -0700911 return;
912 }
913 // Collections don't include the static data added by SubRoute
914 // because it has a duplicate entry for members
915 asyncResp->res.jsonValue["@odata.type"] =
916 "#LogServiceCollection.LogServiceCollection";
917 asyncResp->res.jsonValue["@odata.id"] =
918 "/redfish/v1/Systems/system/LogServices";
919 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
920 asyncResp->res.jsonValue["Description"] =
921 "Collection of LogServices for this Computer System";
922 nlohmann::json& logServiceArray =
923 asyncResp->res.jsonValue["Members"];
924 logServiceArray = nlohmann::json::array();
Ed Tanous14766872022-03-15 10:44:42 -0700925 nlohmann::json::object_t eventLog;
926 eventLog["@odata.id"] =
927 "/redfish/v1/Systems/system/LogServices/EventLog";
928 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500929#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous14766872022-03-15 10:44:42 -0700930 nlohmann::json::object_t dumpLog;
Ed Tanousc08f0322022-05-16 09:25:51 -0700931 dumpLog["@odata.id"] =
Ed Tanous14766872022-03-15 10:44:42 -0700932 "/redfish/v1/Systems/system/LogServices/Dump";
933 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600934#endif
935
Jason M. Billsd53dd412019-02-12 17:16:22 -0800936#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous14766872022-03-15 10:44:42 -0700937 nlohmann::json::object_t crashdump;
938 crashdump["@odata.id"] =
939 "/redfish/v1/Systems/system/LogServices/Crashdump";
940 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800941#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800942
943#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous14766872022-03-15 10:44:42 -0700944 nlohmann::json::object_t hostlogger;
945 hostlogger["@odata.id"] =
946 "/redfish/v1/Systems/system/LogServices/HostLogger";
947 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800948#endif
Ed Tanous45ca1b82022-03-25 13:07:27 -0700949 asyncResp->res.jsonValue["Members@odata.count"] =
950 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800951
Ed Tanous45ca1b82022-03-25 13:07:27 -0700952 crow::connections::systemBus->async_method_call(
953 [asyncResp](const boost::system::error_code ec,
954 const dbus::utility::MapperGetSubTreePathsResponse&
955 subtreePath) {
956 if (ec)
957 {
958 BMCWEB_LOG_ERROR << ec;
959 return;
960 }
961
962 for (const auto& pathStr : subtreePath)
963 {
964 if (pathStr.find("PostCode") != std::string::npos)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700965 {
Ed Tanous45ca1b82022-03-25 13:07:27 -0700966 nlohmann::json& logServiceArrayLocal =
967 asyncResp->res.jsonValue["Members"];
968 logServiceArrayLocal.push_back(
969 {{"@odata.id",
970 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
971 asyncResp->res.jsonValue["Members@odata.count"] =
972 logServiceArrayLocal.size();
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700973 return;
974 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700975 }
976 },
977 "xyz.openbmc_project.ObjectMapper",
978 "/xyz/openbmc_project/object_mapper",
979 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
980 std::array<const char*, 1>{postCodeIface});
981 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700982}
983
984inline void requestRoutesEventLogService(App& app)
985{
986 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700987 .privileges(redfish::privileges::getLogService)
Ed Tanous45ca1b82022-03-25 13:07:27 -0700988 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
989 const std::shared_ptr<
990 bmcweb::AsyncResp>&
991 asyncResp) {
992 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
993 {
994 return;
995 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700996 asyncResp->res.jsonValue["@odata.id"] =
997 "/redfish/v1/Systems/system/LogServices/EventLog";
998 asyncResp->res.jsonValue["@odata.type"] =
999 "#LogService.v1_1_0.LogService";
1000 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1001 asyncResp->res.jsonValue["Description"] =
1002 "System Event Log Service";
1003 asyncResp->res.jsonValue["Id"] = "EventLog";
1004 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301005
1006 std::pair<std::string, std::string> redfishDateTimeOffset =
1007 crow::utility::getDateTimeOffsetNow();
1008
1009 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1010 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1011 redfishDateTimeOffset.second;
1012
Ed Tanous14766872022-03-15 10:44:42 -07001013 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1014 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001015 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1016
George Liu0fda0f12021-11-16 10:06:17 +08001017 {"target",
1018 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001019 });
1020}
1021
1022inline void requestRoutesJournalEventLogClear(App& app)
1023{
Jason M. Bills4978b632022-02-22 14:17:43 -08001024 BMCWEB_ROUTE(
1025 app,
1026 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001027 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001028 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001029 [&app](const crow::Request& req,
1030 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1031 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1032 {
1033 return;
1034 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001035 // Clear the EventLog by deleting the log files
1036 std::vector<std::filesystem::path> redfishLogFiles;
1037 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001038 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001039 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001040 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001041 std::error_code ec;
1042 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001043 }
1044 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001045
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001046 // Reload rsyslog so it knows to start new log files
1047 crow::connections::systemBus->async_method_call(
1048 [asyncResp](const boost::system::error_code ec) {
1049 if (ec)
1050 {
1051 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1052 << ec;
1053 messages::internalError(asyncResp->res);
1054 return;
1055 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001056
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001057 messages::success(asyncResp->res);
1058 },
1059 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1060 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1061 "rsyslog.service", "replace");
1062 });
1063}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001064
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001065static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001066 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001067 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001068{
Jason M. Bills95820182019-04-22 16:25:34 -07001069 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001070 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001071 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001072 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001073 {
1074 return 1;
1075 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001076 std::string timestamp = logEntry.substr(0, space);
1077 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001078 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001079 if (entryStart == std::string::npos)
1080 {
1081 return 1;
1082 }
1083 std::string_view entry(logEntry);
1084 entry.remove_prefix(entryStart);
1085 // Use split to separate the entry into its fields
1086 std::vector<std::string> logEntryFields;
1087 boost::split(logEntryFields, entry, boost::is_any_of(","),
1088 boost::token_compress_on);
1089 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001090 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001091 {
1092 return 1;
1093 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001094 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001095
Jason M. Bills4851d452019-03-28 11:27:48 -07001096 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001097 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001098
Sui Chen54417b02022-03-24 14:59:52 -07001099 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001100 {
Sui Chen54417b02022-03-24 14:59:52 -07001101 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
1102 return 0;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001103 }
1104
Sui Chen54417b02022-03-24 14:59:52 -07001105 std::string msg = message->message;
1106
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001107 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001108 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001109 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001110 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001111 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001112 // If the first string is empty, assume there are no MessageArgs
1113 std::size_t messageArgsSize = 0;
1114 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001115 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001116 messageArgsSize = logEntryFields.size() - 1;
1117 }
1118
Ed Tanous23a21a12020-07-25 04:45:05 +00001119 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001120
1121 // Fill the MessageArgs into the Message
1122 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001123 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001124 {
1125 std::string argStr = "%" + std::to_string(++i);
1126 size_t argPos = msg.find(argStr);
1127 if (argPos != std::string::npos)
1128 {
1129 msg.replace(argPos, argStr.length(), messageArg);
1130 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001131 }
1132 }
1133
Jason M. Bills95820182019-04-22 16:25:34 -07001134 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1135 // format which matches the Redfish format except for the fractional seconds
1136 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001137 std::size_t dot = timestamp.find_first_of('.');
1138 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001139 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001140 {
Jason M. Bills95820182019-04-22 16:25:34 -07001141 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001142 }
1143
1144 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001145 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001146 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001147 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001148 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001149 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001150 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001151 {"Id", logEntryID},
1152 {"Message", std::move(msg)},
1153 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001154 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001155 {"EntryType", "Event"},
Sui Chen54417b02022-03-24 14:59:52 -07001156 {"Severity", message->messageSeverity},
Jason M. Bills95820182019-04-22 16:25:34 -07001157 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001158 return 0;
1159}
1160
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001161inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001162{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001163 BMCWEB_ROUTE(app,
1164 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001165 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001166 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1167 const std::shared_ptr<
1168 bmcweb::AsyncResp>&
1169 asyncResp) {
Ed Tanousc937d2b2022-04-05 09:58:00 -07001170 query_param::QueryCapabilities capabilities = {
1171 .canDelegateTop = true,
1172 .canDelegateSkip = true,
1173 };
1174 query_param::Query delegatedQuery;
1175 if (!redfish::setUpRedfishRouteWithDelegation(
1176 app, req, asyncResp->res, delegatedQuery, capabilities))
Jason M. Bills4978b632022-02-22 14:17:43 -08001177 {
1178 return;
1179 }
1180 // Collections don't include the static data added by SubRoute
1181 // because it has a duplicate entry for members
1182 asyncResp->res.jsonValue["@odata.type"] =
1183 "#LogEntryCollection.LogEntryCollection";
1184 asyncResp->res.jsonValue["@odata.id"] =
1185 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1186 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1187 asyncResp->res.jsonValue["Description"] =
1188 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001189
Jason M. Bills4978b632022-02-22 14:17:43 -08001190 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1191 logEntryArray = nlohmann::json::array();
1192 // Go through the log files and create a unique ID for each
1193 // entry
1194 std::vector<std::filesystem::path> redfishLogFiles;
1195 getRedfishLogFiles(redfishLogFiles);
1196 uint64_t entryCount = 0;
1197 std::string logEntry;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001198
Jason M. Bills4978b632022-02-22 14:17:43 -08001199 // Oldest logs are in the last file, so start there and loop
1200 // backwards
1201 for (auto it = redfishLogFiles.rbegin();
1202 it < redfishLogFiles.rend(); it++)
1203 {
1204 std::ifstream logStream(*it);
1205 if (!logStream.is_open())
Andrew Geisslercb92c032018-08-17 07:56:14 -07001206 {
Jason M. Bills4978b632022-02-22 14:17:43 -08001207 continue;
1208 }
1209
1210 // Reset the unique ID on the first entry
1211 bool firstEntry = true;
1212 while (std::getline(logStream, logEntry))
1213 {
1214 entryCount++;
1215 // Handle paging using skip (number of entries to skip
1216 // from the start) and top (number of entries to
1217 // display)
Ed Tanousc937d2b2022-04-05 09:58:00 -07001218 if (entryCount <= delegatedQuery.skip ||
1219 entryCount > delegatedQuery.skip + delegatedQuery.top)
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001220 {
1221 continue;
1222 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001223
Jason M. Bills4978b632022-02-22 14:17:43 -08001224 std::string idStr;
1225 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001226 {
Jason M. Bills4978b632022-02-22 14:17:43 -08001227 continue;
1228 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001229
Jason M. Bills4978b632022-02-22 14:17:43 -08001230 if (firstEntry)
1231 {
1232 firstEntry = false;
1233 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001234
Jason M. Bills4978b632022-02-22 14:17:43 -08001235 logEntryArray.push_back({});
1236 nlohmann::json& bmcLogEntry = logEntryArray.back();
1237 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) !=
1238 0)
1239 {
1240 messages::internalError(asyncResp->res);
1241 return;
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001242 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001243 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001244 }
1245 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanousc937d2b2022-04-05 09:58:00 -07001246 if (delegatedQuery.skip + delegatedQuery.top < entryCount)
Jason M. Bills4978b632022-02-22 14:17:43 -08001247 {
1248 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1249 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
Ed Tanousc937d2b2022-04-05 09:58:00 -07001250 std::to_string(delegatedQuery.skip + delegatedQuery.top);
Jason M. Bills4978b632022-02-22 14:17:43 -08001251 }
1252 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001253}
Chicago Duan336e96c2019-07-15 14:22:08 +08001254
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001255inline void requestRoutesJournalEventLogEntry(App& app)
1256{
1257 BMCWEB_ROUTE(
1258 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001259 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001260 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001261 [&app](const crow::Request& req,
1262 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1263 const std::string& param) {
1264 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1265 {
1266 return;
1267 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001268 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001269
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001270 // Go through the log files and check the unique ID for each
1271 // entry to find the target entry
1272 std::vector<std::filesystem::path> redfishLogFiles;
1273 getRedfishLogFiles(redfishLogFiles);
1274 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001275
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001276 // Oldest logs are in the last file, so start there and loop
1277 // backwards
1278 for (auto it = redfishLogFiles.rbegin();
1279 it < redfishLogFiles.rend(); it++)
1280 {
1281 std::ifstream logStream(*it);
1282 if (!logStream.is_open())
1283 {
1284 continue;
1285 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001286
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001287 // Reset the unique ID on the first entry
1288 bool firstEntry = true;
1289 while (std::getline(logStream, logEntry))
1290 {
1291 std::string idStr;
1292 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1293 {
1294 continue;
1295 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001296
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001297 if (firstEntry)
1298 {
1299 firstEntry = false;
1300 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001301
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001302 if (idStr == targetID)
1303 {
1304 if (fillEventLogEntryJson(
1305 idStr, logEntry,
1306 asyncResp->res.jsonValue) != 0)
1307 {
1308 messages::internalError(asyncResp->res);
1309 return;
1310 }
1311 return;
1312 }
1313 }
1314 }
1315 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07001316 messages::resourceMissingAtURI(
1317 asyncResp->res, crow::utility::urlFromPieces(targetID));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001318 });
1319}
1320
1321inline void requestRoutesDBusEventLogEntryCollection(App& app)
1322{
1323 BMCWEB_ROUTE(app,
1324 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001325 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001326 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1327 const std::shared_ptr<
1328 bmcweb::AsyncResp>&
1329 asyncResp) {
1330 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1331 {
1332 return;
1333 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001334 // Collections don't include the static data added by SubRoute
1335 // because it has a duplicate entry for members
1336 asyncResp->res.jsonValue["@odata.type"] =
1337 "#LogEntryCollection.LogEntryCollection";
1338 asyncResp->res.jsonValue["@odata.id"] =
1339 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1340 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1341 asyncResp->res.jsonValue["Description"] =
1342 "Collection of System Event Log Entries";
1343
1344 // DBus implementation of EventLog/Entries
1345 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001346 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001347 [asyncResp](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001348 const dbus::utility::ManagedObjectType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001349 if (ec)
1350 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001351 // TODO Handle for specific error code
1352 BMCWEB_LOG_ERROR
1353 << "getLogEntriesIfaceData resp_handler got error "
1354 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001355 messages::internalError(asyncResp->res);
1356 return;
1357 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001358 nlohmann::json& entriesArray =
1359 asyncResp->res.jsonValue["Members"];
1360 entriesArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -08001361 for (const auto& objectPath : resp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001362 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001363 const uint32_t* id = nullptr;
Ed Tanousc419c752022-01-26 12:19:54 -08001364 const uint64_t* timestamp = nullptr;
1365 const uint64_t* updateTimestamp = nullptr;
Ed Tanous914e2d52022-01-07 11:38:34 -08001366 const std::string* severity = nullptr;
1367 const std::string* message = nullptr;
1368 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001369 bool resolved = false;
Ed Tanous9eb808c2022-01-25 10:19:23 -08001370 for (const auto& interfaceMap : objectPath.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001371 {
1372 if (interfaceMap.first ==
1373 "xyz.openbmc_project.Logging.Entry")
1374 {
Ed Tanous9eb808c2022-01-25 10:19:23 -08001375 for (const auto& propertyMap :
1376 interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001377 {
1378 if (propertyMap.first == "Id")
1379 {
1380 id = std::get_if<uint32_t>(
1381 &propertyMap.second);
1382 }
1383 else if (propertyMap.first == "Timestamp")
1384 {
Ed Tanousc419c752022-01-26 12:19:54 -08001385 timestamp = std::get_if<uint64_t>(
1386 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001387 }
1388 else if (propertyMap.first ==
1389 "UpdateTimestamp")
1390 {
Ed Tanousc419c752022-01-26 12:19:54 -08001391 updateTimestamp = std::get_if<uint64_t>(
1392 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001393 }
1394 else if (propertyMap.first == "Severity")
1395 {
1396 severity = std::get_if<std::string>(
1397 &propertyMap.second);
1398 }
1399 else if (propertyMap.first == "Message")
1400 {
1401 message = std::get_if<std::string>(
1402 &propertyMap.second);
1403 }
1404 else if (propertyMap.first == "Resolved")
1405 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001406 const bool* resolveptr =
1407 std::get_if<bool>(
1408 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001409 if (resolveptr == nullptr)
1410 {
1411 messages::internalError(
1412 asyncResp->res);
1413 return;
1414 }
1415 resolved = *resolveptr;
1416 }
1417 }
1418 if (id == nullptr || message == nullptr ||
1419 severity == nullptr)
1420 {
1421 messages::internalError(asyncResp->res);
1422 return;
1423 }
1424 }
1425 else if (interfaceMap.first ==
1426 "xyz.openbmc_project.Common.FilePath")
1427 {
Ed Tanous9eb808c2022-01-25 10:19:23 -08001428 for (const auto& propertyMap :
1429 interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001430 {
1431 if (propertyMap.first == "Path")
1432 {
1433 filePath = std::get_if<std::string>(
1434 &propertyMap.second);
1435 }
1436 }
1437 }
1438 }
1439 // Object path without the
1440 // xyz.openbmc_project.Logging.Entry interface, ignore
1441 // and continue.
1442 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001443 severity == nullptr || timestamp == nullptr ||
1444 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001445 {
1446 continue;
1447 }
1448 entriesArray.push_back({});
1449 nlohmann::json& thisEntry = entriesArray.back();
1450 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1451 thisEntry["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001452 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001453 std::to_string(*id);
1454 thisEntry["Name"] = "System Event Log Entry";
1455 thisEntry["Id"] = std::to_string(*id);
1456 thisEntry["Message"] = *message;
1457 thisEntry["Resolved"] = resolved;
1458 thisEntry["EntryType"] = "Event";
1459 thisEntry["Severity"] =
1460 translateSeverityDbusToRedfish(*severity);
1461 thisEntry["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001462 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001463 thisEntry["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001464 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001465 if (filePath != nullptr)
1466 {
1467 thisEntry["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001468 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001469 std::to_string(*id) + "/attachment";
1470 }
1471 }
1472 std::sort(entriesArray.begin(), entriesArray.end(),
1473 [](const nlohmann::json& left,
1474 const nlohmann::json& right) {
1475 return (left["Id"] <= right["Id"]);
1476 });
1477 asyncResp->res.jsonValue["Members@odata.count"] =
1478 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001479 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001480 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1481 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1482 });
1483}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001484
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001485inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001486{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001487 BMCWEB_ROUTE(
1488 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001489 .privileges(redfish::privileges::getLogEntry)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001490 .methods(
1491 boost::beast::http::verb::
1492 get)([&app](const crow::Request& req,
1493 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1494 const std::string& param) {
1495 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001496 {
Ed Tanous45ca1b82022-03-25 13:07:27 -07001497 return;
1498 }
1499 std::string entryID = param;
1500 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001501
Ed Tanous45ca1b82022-03-25 13:07:27 -07001502 // DBus implementation of EventLog/Entries
1503 // Make call to Logging Service to find all log entry objects
1504 crow::connections::systemBus->async_method_call(
1505 [asyncResp,
1506 entryID](const boost::system::error_code ec,
1507 const dbus::utility::DBusPropertiesMap& resp) {
1508 if (ec.value() == EBADR)
1509 {
1510 messages::resourceNotFound(asyncResp->res,
1511 "EventLogEntry", entryID);
1512 return;
1513 }
1514 if (ec)
1515 {
1516 BMCWEB_LOG_ERROR
1517 << "EventLogEntry (DBus) resp_handler got error "
1518 << ec;
1519 messages::internalError(asyncResp->res);
1520 return;
1521 }
1522 const uint32_t* id = nullptr;
1523 const uint64_t* timestamp = nullptr;
1524 const uint64_t* updateTimestamp = nullptr;
1525 const std::string* severity = nullptr;
1526 const std::string* message = nullptr;
1527 const std::string* filePath = nullptr;
1528 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001529
Ed Tanous45ca1b82022-03-25 13:07:27 -07001530 for (const auto& propertyMap : resp)
1531 {
1532 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001533 {
Ed Tanous45ca1b82022-03-25 13:07:27 -07001534 id = std::get_if<uint32_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001535 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07001536 else if (propertyMap.first == "Timestamp")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001537 {
Ed Tanous45ca1b82022-03-25 13:07:27 -07001538 timestamp =
1539 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001540 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07001541 else if (propertyMap.first == "UpdateTimestamp")
1542 {
1543 updateTimestamp =
1544 std::get_if<uint64_t>(&propertyMap.second);
1545 }
1546 else if (propertyMap.first == "Severity")
1547 {
1548 severity =
1549 std::get_if<std::string>(&propertyMap.second);
1550 }
1551 else if (propertyMap.first == "Message")
1552 {
1553 message =
1554 std::get_if<std::string>(&propertyMap.second);
1555 }
1556 else if (propertyMap.first == "Resolved")
1557 {
1558 const bool* resolveptr =
1559 std::get_if<bool>(&propertyMap.second);
1560 if (resolveptr == nullptr)
1561 {
1562 messages::internalError(asyncResp->res);
1563 return;
1564 }
1565 resolved = *resolveptr;
1566 }
1567 else if (propertyMap.first == "Path")
1568 {
1569 filePath =
1570 std::get_if<std::string>(&propertyMap.second);
1571 }
1572 }
1573 if (id == nullptr || message == nullptr ||
1574 severity == nullptr || timestamp == nullptr ||
1575 updateTimestamp == nullptr)
1576 {
1577 messages::internalError(asyncResp->res);
1578 return;
1579 }
1580 asyncResp->res.jsonValue["@odata.type"] =
1581 "#LogEntry.v1_8_0.LogEntry";
1582 asyncResp->res.jsonValue["@odata.id"] =
1583 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1584 std::to_string(*id);
1585 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1586 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1587 asyncResp->res.jsonValue["Message"] = *message;
1588 asyncResp->res.jsonValue["Resolved"] = resolved;
1589 asyncResp->res.jsonValue["EntryType"] = "Event";
1590 asyncResp->res.jsonValue["Severity"] =
1591 translateSeverityDbusToRedfish(*severity);
1592 asyncResp->res.jsonValue["Created"] =
1593 crow::utility::getDateTimeUintMs(*timestamp);
1594 asyncResp->res.jsonValue["Modified"] =
1595 crow::utility::getDateTimeUintMs(*updateTimestamp);
1596 if (filePath != nullptr)
1597 {
1598 asyncResp->res.jsonValue["AdditionalDataURI"] =
Potin Laie7dbd532022-05-05 16:31:59 +08001599 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1600 std::to_string(*id) + "/attachment";
Ed Tanous45ca1b82022-03-25 13:07:27 -07001601 }
1602 },
1603 "xyz.openbmc_project.Logging",
1604 "/xyz/openbmc_project/logging/entry/" + entryID,
1605 "org.freedesktop.DBus.Properties", "GetAll", "");
1606 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001607
1608 BMCWEB_ROUTE(
1609 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001610 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001611 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001612 [&app](const crow::Request& req,
1613 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1614 const std::string& entryId) {
1615 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1616 {
1617 return;
1618 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001619 std::optional<bool> resolved;
1620
Willy Tu15ed6782021-12-14 11:03:16 -08001621 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1622 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001623 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001624 return;
1625 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001626 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001627
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001628 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001629 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001630 if (ec)
1631 {
1632 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1633 messages::internalError(asyncResp->res);
1634 return;
1635 }
1636 },
1637 "xyz.openbmc_project.Logging",
1638 "/xyz/openbmc_project/logging/entry/" + entryId,
1639 "org.freedesktop.DBus.Properties", "Set",
1640 "xyz.openbmc_project.Logging.Entry", "Resolved",
Ed Tanous168e20c2021-12-13 14:39:53 -08001641 dbus::utility::DbusVariantType(*resolved));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001642 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001643
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001644 BMCWEB_ROUTE(
1645 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001646 .privileges(redfish::privileges::deleteLogEntry)
1647
Ed Tanous45ca1b82022-03-25 13:07:27 -07001648 .methods(boost::beast::http::verb::
1649 delete_)([&app](const crow::Request& req,
1650 const std::shared_ptr<bmcweb::AsyncResp>&
1651 asyncResp,
1652 const std::string& param) {
1653 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001654 {
Ed Tanous45ca1b82022-03-25 13:07:27 -07001655 return;
1656 }
1657 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001658
Ed Tanous45ca1b82022-03-25 13:07:27 -07001659 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001660
Ed Tanous45ca1b82022-03-25 13:07:27 -07001661 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001662
Ed Tanous45ca1b82022-03-25 13:07:27 -07001663 // Process response from Logging service.
1664 auto respHandler = [asyncResp,
1665 entryID](const boost::system::error_code ec) {
1666 BMCWEB_LOG_DEBUG
1667 << "EventLogEntry (DBus) doDelete callback: Done";
1668 if (ec)
1669 {
1670 if (ec.value() == EBADR)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001671 {
Ed Tanous45ca1b82022-03-25 13:07:27 -07001672 messages::resourceNotFound(asyncResp->res, "LogEntry",
1673 entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001674 return;
1675 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07001676 // TODO Handle for specific error code
1677 BMCWEB_LOG_ERROR
1678 << "EventLogEntry (DBus) doDelete respHandler got error "
1679 << ec;
1680 asyncResp->res.result(
1681 boost::beast::http::status::internal_server_error);
1682 return;
1683 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001684
Ed Tanous45ca1b82022-03-25 13:07:27 -07001685 asyncResp->res.result(boost::beast::http::status::ok);
1686 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001687
Ed Tanous45ca1b82022-03-25 13:07:27 -07001688 // Make call to Logging service to request Delete Log
1689 crow::connections::systemBus->async_method_call(
1690 respHandler, "xyz.openbmc_project.Logging",
1691 "/xyz/openbmc_project/logging/entry/" + entryID,
1692 "xyz.openbmc_project.Object.Delete", "Delete");
1693 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001694}
1695
1696inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001697{
George Liu0fda0f12021-11-16 10:06:17 +08001698 BMCWEB_ROUTE(
1699 app,
1700 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001701 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001702 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001703 [&app](const crow::Request& req,
1704 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1705 const std::string& param) {
1706 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1707 {
1708 return;
1709 }
George Liu647b3cd2021-07-05 12:43:56 +08001710 if (!http_helpers::isOctetAccepted(
1711 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001712 {
1713 asyncResp->res.result(
1714 boost::beast::http::status::bad_request);
1715 return;
1716 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001717
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001718 std::string entryID = param;
1719 dbus::utility::escapePathForDbus(entryID);
1720
1721 crow::connections::systemBus->async_method_call(
1722 [asyncResp,
1723 entryID](const boost::system::error_code ec,
1724 const sdbusplus::message::unix_fd& unixfd) {
1725 if (ec.value() == EBADR)
1726 {
1727 messages::resourceNotFound(
1728 asyncResp->res, "EventLogAttachment", entryID);
1729 return;
1730 }
1731 if (ec)
1732 {
1733 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1734 messages::internalError(asyncResp->res);
1735 return;
1736 }
1737
1738 int fd = -1;
1739 fd = dup(unixfd);
1740 if (fd == -1)
1741 {
1742 messages::internalError(asyncResp->res);
1743 return;
1744 }
1745
1746 long long int size = lseek(fd, 0, SEEK_END);
1747 if (size == -1)
1748 {
1749 messages::internalError(asyncResp->res);
1750 return;
1751 }
1752
1753 // Arbitrary max size of 64kb
1754 constexpr int maxFileSize = 65536;
1755 if (size > maxFileSize)
1756 {
1757 BMCWEB_LOG_ERROR
1758 << "File size exceeds maximum allowed size of "
1759 << maxFileSize;
1760 messages::internalError(asyncResp->res);
1761 return;
1762 }
1763 std::vector<char> data(static_cast<size_t>(size));
1764 long long int rc = lseek(fd, 0, SEEK_SET);
1765 if (rc == -1)
1766 {
1767 messages::internalError(asyncResp->res);
1768 return;
1769 }
1770 rc = read(fd, data.data(), data.size());
1771 if ((rc == -1) || (rc != size))
1772 {
1773 messages::internalError(asyncResp->res);
1774 return;
1775 }
1776 close(fd);
1777
1778 std::string_view strData(data.data(), data.size());
1779 std::string output =
1780 crow::utility::base64encode(strData);
1781
1782 asyncResp->res.addHeader("Content-Type",
1783 "application/octet-stream");
1784 asyncResp->res.addHeader("Content-Transfer-Encoding",
1785 "Base64");
1786 asyncResp->res.body() = std::move(output);
1787 },
1788 "xyz.openbmc_project.Logging",
1789 "/xyz/openbmc_project/logging/entry/" + entryID,
1790 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1791 });
1792}
1793
Spencer Kub7028eb2021-10-26 15:27:35 +08001794constexpr const char* hostLoggerFolderPath = "/var/log/console";
1795
1796inline bool
1797 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1798 std::vector<std::filesystem::path>& hostLoggerFiles)
1799{
1800 std::error_code ec;
1801 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1802 if (ec)
1803 {
1804 BMCWEB_LOG_ERROR << ec.message();
1805 return false;
1806 }
1807 for (const std::filesystem::directory_entry& it : logPath)
1808 {
1809 std::string filename = it.path().filename();
1810 // Prefix of each log files is "log". Find the file and save the
1811 // path
1812 if (boost::starts_with(filename, "log"))
1813 {
1814 hostLoggerFiles.emplace_back(it.path());
1815 }
1816 }
1817 // As the log files rotate, they are appended with a ".#" that is higher for
1818 // the older logs. Since we start from oldest logs, sort the name in
1819 // descending order.
1820 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1821 AlphanumLess<std::string>());
1822
1823 return true;
1824}
1825
1826inline bool
1827 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
Ed Tanousc937d2b2022-04-05 09:58:00 -07001828 uint64_t skip, uint64_t top,
Spencer Kub7028eb2021-10-26 15:27:35 +08001829 std::vector<std::string>& logEntries, size_t& logCount)
1830{
1831 GzFileReader logFile;
1832
1833 // Go though all log files and expose host logs.
1834 for (const std::filesystem::path& it : hostLoggerFiles)
1835 {
1836 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1837 {
1838 BMCWEB_LOG_ERROR << "fail to expose host logs";
1839 return false;
1840 }
1841 }
1842 // Get lastMessage from constructor by getter
1843 std::string lastMessage = logFile.getLastMessage();
1844 if (!lastMessage.empty())
1845 {
1846 logCount++;
1847 if (logCount > skip && logCount <= (skip + top))
1848 {
1849 logEntries.push_back(lastMessage);
1850 }
1851 }
1852 return true;
1853}
1854
1855inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1856 const std::string& msg,
1857 nlohmann::json& logEntryJson)
1858{
1859 // Fill in the log entry with the gathered data.
1860 logEntryJson = {
1861 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1862 {"@odata.id",
1863 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1864 logEntryID},
1865 {"Name", "Host Logger Entry"},
1866 {"Id", logEntryID},
1867 {"Message", msg},
1868 {"EntryType", "Oem"},
1869 {"Severity", "OK"},
1870 {"OemRecordFormat", "Host Logger Entry"}};
1871}
1872
1873inline void requestRoutesSystemHostLogger(App& app)
1874{
1875 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1876 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001877 .methods(boost::beast::http::verb::get)(
1878 [&app](const crow::Request& req,
1879 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1880 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1881 {
1882 return;
1883 }
1884 asyncResp->res.jsonValue["@odata.id"] =
1885 "/redfish/v1/Systems/system/LogServices/HostLogger";
1886 asyncResp->res.jsonValue["@odata.type"] =
1887 "#LogService.v1_1_0.LogService";
1888 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1889 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1890 asyncResp->res.jsonValue["Id"] = "HostLogger";
1891 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1892 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1893 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001894}
1895
1896inline void requestRoutesSystemHostLoggerCollection(App& app)
1897{
1898 BMCWEB_ROUTE(app,
1899 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1900 .privileges(redfish::privileges::getLogEntry)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001901 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
1902 const std::shared_ptr<
1903 bmcweb::AsyncResp>&
1904 asyncResp) {
Ed Tanousc937d2b2022-04-05 09:58:00 -07001905 query_param::QueryCapabilities capabilities = {
1906 .canDelegateTop = true,
1907 .canDelegateSkip = true,
1908 };
1909 query_param::Query delegatedQuery;
1910 if (!redfish::setUpRedfishRouteWithDelegation(
1911 app, req, asyncResp->res, delegatedQuery, capabilities))
George Liu0fda0f12021-11-16 10:06:17 +08001912 {
1913 return;
1914 }
1915 asyncResp->res.jsonValue["@odata.id"] =
1916 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1917 asyncResp->res.jsonValue["@odata.type"] =
1918 "#LogEntryCollection.LogEntryCollection";
1919 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1920 asyncResp->res.jsonValue["Description"] =
1921 "Collection of HostLogger Entries";
1922 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1923 logEntryArray = nlohmann::json::array();
1924 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001925
George Liu0fda0f12021-11-16 10:06:17 +08001926 std::vector<std::filesystem::path> hostLoggerFiles;
1927 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1928 {
1929 BMCWEB_LOG_ERROR << "fail to get host log file path";
1930 return;
1931 }
1932
1933 size_t logCount = 0;
1934 // This vector only store the entries we want to expose that
1935 // control by skip and top.
1936 std::vector<std::string> logEntries;
Ed Tanousc937d2b2022-04-05 09:58:00 -07001937 if (!getHostLoggerEntries(hostLoggerFiles, delegatedQuery.skip,
1938 delegatedQuery.top, logEntries, logCount))
George Liu0fda0f12021-11-16 10:06:17 +08001939 {
1940 messages::internalError(asyncResp->res);
1941 return;
1942 }
1943 // If vector is empty, that means skip value larger than total
1944 // log count
Ed Tanous26f69762022-01-25 09:49:11 -08001945 if (logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001946 {
1947 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1948 return;
1949 }
Ed Tanous26f69762022-01-25 09:49:11 -08001950 if (!logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001951 {
1952 for (size_t i = 0; i < logEntries.size(); i++)
Spencer Kub7028eb2021-10-26 15:27:35 +08001953 {
George Liu0fda0f12021-11-16 10:06:17 +08001954 logEntryArray.push_back({});
1955 nlohmann::json& hostLogEntry = logEntryArray.back();
Ed Tanousc937d2b2022-04-05 09:58:00 -07001956 fillHostLoggerEntryJson(
1957 std::to_string(delegatedQuery.skip + i), logEntries[i],
1958 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;
Ed Tanousc937d2b2022-04-05 09:58:00 -07001962 if (delegatedQuery.skip + delegatedQuery.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=" +
Ed Tanousc937d2b2022-04-05 09:58:00 -07001966 std::to_string(delegatedQuery.skip +
1967 delegatedQuery.top);
Spencer Kub7028eb2021-10-26 15:27:35 +08001968 }
George Liu0fda0f12021-11-16 10:06:17 +08001969 }
1970 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001971}
1972
1973inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1974{
1975 BMCWEB_ROUTE(
1976 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1977 .privileges(redfish::privileges::getLogEntry)
1978 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001979 [&app](const crow::Request& req,
1980 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1981 const std::string& param) {
1982 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1983 {
1984 return;
1985 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001986 const std::string& targetID = param;
1987
1988 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001989
1990 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1991 const char* end = targetID.data() + targetID.size();
1992
1993 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
Spencer Kub7028eb2021-10-26 15:27:35 +08001994 if (ec == std::errc::invalid_argument)
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 if (ec == std::errc::result_out_of_range)
2000 {
Ed Tanousace85d62021-10-26 12:45:59 -07002001 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
Spencer Kub7028eb2021-10-26 15:27:35 +08002002 return;
2003 }
2004
2005 std::vector<std::filesystem::path> hostLoggerFiles;
2006 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2007 {
2008 BMCWEB_LOG_ERROR << "fail to get host log file path";
2009 return;
2010 }
2011
2012 size_t logCount = 0;
2013 uint64_t top = 1;
2014 std::vector<std::string> logEntries;
2015 // We can get specific entry by skip and top. For example, if we
2016 // want to get nth entry, we can set skip = n-1 and top = 1 to
2017 // get that entry
2018 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
2019 logEntries, logCount))
2020 {
2021 messages::internalError(asyncResp->res);
2022 return;
2023 }
2024
2025 if (!logEntries.empty())
2026 {
2027 fillHostLoggerEntryJson(targetID, logEntries[0],
2028 asyncResp->res.jsonValue);
2029 return;
2030 }
2031
2032 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07002033 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
Spencer Kub7028eb2021-10-26 15:27:35 +08002034 });
2035}
2036
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002037inline void requestRoutesBMCLogServiceCollection(App& app)
2038{
2039 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002040 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002041 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002042 [&app](const crow::Request& req,
2043 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2044 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2045 {
2046 return;
2047 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002048 // Collections don't include the static data added by SubRoute
2049 // because it has a duplicate entry for members
2050 asyncResp->res.jsonValue["@odata.type"] =
2051 "#LogServiceCollection.LogServiceCollection";
2052 asyncResp->res.jsonValue["@odata.id"] =
2053 "/redfish/v1/Managers/bmc/LogServices";
2054 asyncResp->res.jsonValue["Name"] =
2055 "Open BMC Log Services Collection";
2056 asyncResp->res.jsonValue["Description"] =
2057 "Collection of LogServices for this Manager";
2058 nlohmann::json& logServiceArray =
2059 asyncResp->res.jsonValue["Members"];
2060 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002061#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002062 logServiceArray.push_back(
2063 {{"@odata.id",
2064 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002065#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002066#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002067 logServiceArray.push_back(
2068 {{"@odata.id",
2069 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002070#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002071 asyncResp->res.jsonValue["Members@odata.count"] =
2072 logServiceArray.size();
2073 });
2074}
Ed Tanous1da66f72018-07-27 16:13:37 -07002075
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002076inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002077{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002078 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002079 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002080 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002081 [&app](const crow::Request& req,
2082 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2083 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2084 {
2085 return;
2086 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002087 asyncResp->res.jsonValue["@odata.type"] =
2088 "#LogService.v1_1_0.LogService";
2089 asyncResp->res.jsonValue["@odata.id"] =
2090 "/redfish/v1/Managers/bmc/LogServices/Journal";
2091 asyncResp->res.jsonValue["Name"] =
2092 "Open BMC Journal Log Service";
2093 asyncResp->res.jsonValue["Description"] =
2094 "BMC Journal Log Service";
2095 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2096 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302097
2098 std::pair<std::string, std::string> redfishDateTimeOffset =
2099 crow::utility::getDateTimeOffsetNow();
2100 asyncResp->res.jsonValue["DateTime"] =
2101 redfishDateTimeOffset.first;
2102 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2103 redfishDateTimeOffset.second;
2104
Ed Tanous14766872022-03-15 10:44:42 -07002105 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2106 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002107 });
2108}
Jason M. Billse1f26342018-07-18 12:12:00 -07002109
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002110static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2111 sd_journal* journal,
2112 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002113{
2114 // Get the Log Entry contents
2115 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002116
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002117 std::string message;
2118 std::string_view syslogID;
2119 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2120 if (ret < 0)
2121 {
2122 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2123 << strerror(-ret);
2124 }
2125 if (!syslogID.empty())
2126 {
2127 message += std::string(syslogID) + ": ";
2128 }
2129
Ed Tanous39e77502019-03-04 17:35:53 -08002130 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002131 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002132 if (ret < 0)
2133 {
2134 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2135 return 1;
2136 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002137 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002138
2139 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002140 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002141 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002142 if (ret < 0)
2143 {
2144 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002145 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002146
2147 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002148 std::string entryTimeStr;
2149 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002150 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002151 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002152 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002153
2154 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002155 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002156 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002157 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2158 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002159 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002160 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002161 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002162 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002163 {"Severity", severity <= 2 ? "Critical"
2164 : severity <= 4 ? "Warning"
2165 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002166 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002167 {"Created", std::move(entryTimeStr)}};
2168 return 0;
2169}
2170
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002171inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002172{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002173 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002174 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002175 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
2176 const std::shared_ptr<
2177 bmcweb::AsyncResp>&
2178 asyncResp) {
Ed Tanousc937d2b2022-04-05 09:58:00 -07002179 query_param::QueryCapabilities capabilities = {
2180 .canDelegateTop = true,
2181 .canDelegateSkip = true,
2182 };
2183 query_param::Query delegatedQuery;
2184 if (!redfish::setUpRedfishRouteWithDelegation(
2185 app, req, asyncResp->res, delegatedQuery, capabilities))
George Liu0fda0f12021-11-16 10:06:17 +08002186 {
2187 return;
2188 }
2189 // Collections don't include the static data added by SubRoute
2190 // because it has a duplicate entry for members
2191 asyncResp->res.jsonValue["@odata.type"] =
2192 "#LogEntryCollection.LogEntryCollection";
2193 asyncResp->res.jsonValue["@odata.id"] =
2194 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2195 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2196 asyncResp->res.jsonValue["Description"] =
2197 "Collection of BMC Journal Entries";
2198 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2199 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002200
George Liu0fda0f12021-11-16 10:06:17 +08002201 // Go through the journal and use the timestamp to create a
2202 // unique ID for each entry
2203 sd_journal* journalTmp = nullptr;
2204 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2205 if (ret < 0)
2206 {
2207 BMCWEB_LOG_ERROR << "failed to open journal: "
2208 << strerror(-ret);
2209 messages::internalError(asyncResp->res);
2210 return;
2211 }
2212 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2213 journalTmp, sd_journal_close);
2214 journalTmp = nullptr;
2215 uint64_t entryCount = 0;
2216 // Reset the unique ID on the first entry
2217 bool firstEntry = true;
2218 SD_JOURNAL_FOREACH(journal.get())
2219 {
2220 entryCount++;
2221 // Handle paging using skip (number of entries to skip from
2222 // the start) and top (number of entries to display)
Ed Tanousc937d2b2022-04-05 09:58:00 -07002223 if (entryCount <= delegatedQuery.skip ||
2224 entryCount > delegatedQuery.skip + delegatedQuery.top)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002225 {
George Liu0fda0f12021-11-16 10:06:17 +08002226 continue;
2227 }
2228
2229 std::string idStr;
2230 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2231 {
2232 continue;
2233 }
2234
2235 if (firstEntry)
2236 {
2237 firstEntry = false;
2238 }
2239
2240 logEntryArray.push_back({});
2241 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2242 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2243 bmcJournalLogEntry) != 0)
2244 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002245 messages::internalError(asyncResp->res);
2246 return;
2247 }
George Liu0fda0f12021-11-16 10:06:17 +08002248 }
2249 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanousc937d2b2022-04-05 09:58:00 -07002250 if (delegatedQuery.skip + delegatedQuery.top < entryCount)
George Liu0fda0f12021-11-16 10:06:17 +08002251 {
2252 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2253 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Ed Tanousc937d2b2022-04-05 09:58:00 -07002254 std::to_string(delegatedQuery.skip + delegatedQuery.top);
George Liu0fda0f12021-11-16 10:06:17 +08002255 }
2256 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002257}
Jason M. Billse1f26342018-07-18 12:12:00 -07002258
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002259inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002260{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002261 BMCWEB_ROUTE(app,
2262 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002263 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002264 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002265 [&app](const crow::Request& req,
2266 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2267 const std::string& entryID) {
2268 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2269 {
2270 return;
2271 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002272 // Convert the unique ID back to a timestamp to find the entry
2273 uint64_t ts = 0;
2274 uint64_t index = 0;
2275 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2276 {
2277 return;
2278 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002279
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002280 sd_journal* journalTmp = nullptr;
2281 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2282 if (ret < 0)
2283 {
2284 BMCWEB_LOG_ERROR << "failed to open journal: "
2285 << strerror(-ret);
2286 messages::internalError(asyncResp->res);
2287 return;
2288 }
2289 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2290 journal(journalTmp, sd_journal_close);
2291 journalTmp = nullptr;
2292 // Go to the timestamp in the log and move to the entry at the
2293 // index tracking the unique ID
2294 std::string idStr;
2295 bool firstEntry = true;
2296 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2297 if (ret < 0)
2298 {
2299 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2300 << strerror(-ret);
2301 messages::internalError(asyncResp->res);
2302 return;
2303 }
2304 for (uint64_t i = 0; i <= index; i++)
2305 {
2306 sd_journal_next(journal.get());
2307 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2308 {
2309 messages::internalError(asyncResp->res);
2310 return;
2311 }
2312 if (firstEntry)
2313 {
2314 firstEntry = false;
2315 }
2316 }
2317 // Confirm that the entry ID matches what was requested
2318 if (idStr != entryID)
2319 {
Ed Tanousace85d62021-10-26 12:45:59 -07002320 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002321 return;
2322 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002323
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002324 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2325 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002326 {
2327 messages::internalError(asyncResp->res);
2328 return;
2329 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002330 });
2331}
2332
2333inline void requestRoutesBMCDumpService(App& app)
2334{
2335 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002336 .privileges(redfish::privileges::getLogService)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002337 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
2338 const std::shared_ptr<
2339 bmcweb::AsyncResp>&
2340 asyncResp) {
2341 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2342 {
2343 return;
2344 }
George Liu0fda0f12021-11-16 10:06:17 +08002345 asyncResp->res.jsonValue["@odata.id"] =
2346 "/redfish/v1/Managers/bmc/LogServices/Dump";
2347 asyncResp->res.jsonValue["@odata.type"] =
2348 "#LogService.v1_2_0.LogService";
2349 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2350 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2351 asyncResp->res.jsonValue["Id"] = "Dump";
2352 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302353
George Liu0fda0f12021-11-16 10:06:17 +08002354 std::pair<std::string, std::string> redfishDateTimeOffset =
2355 crow::utility::getDateTimeOffsetNow();
2356 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2357 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2358 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302359
Ed Tanous14766872022-03-15 10:44:42 -07002360 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2361 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2362 asyncResp->res
2363 .jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2364 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog";
2365 asyncResp->res
2366 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2367 ["target"] =
2368 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
George Liu0fda0f12021-11-16 10:06:17 +08002369 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002370}
2371
2372inline void requestRoutesBMCDumpEntryCollection(App& app)
2373{
2374
2375 /**
2376 * Functions triggers appropriate requests on DBus
2377 */
2378 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002379 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002380 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002381 [&app](const crow::Request& req,
2382 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2383 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2384 {
2385 return;
2386 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002387 asyncResp->res.jsonValue["@odata.type"] =
2388 "#LogEntryCollection.LogEntryCollection";
2389 asyncResp->res.jsonValue["@odata.id"] =
2390 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2391 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2392 asyncResp->res.jsonValue["Description"] =
2393 "Collection of BMC Dump Entries";
2394
2395 getDumpEntryCollection(asyncResp, "BMC");
2396 });
2397}
2398
2399inline void requestRoutesBMCDumpEntry(App& app)
2400{
2401 BMCWEB_ROUTE(app,
2402 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002403 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002404 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002405 [&app](const crow::Request& req,
2406 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2407 const std::string& param) {
2408 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2409 {
2410 return;
2411 }
2412
2413 getDumpEntryById(app, req, asyncResp, param, "BMC");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002414 });
2415 BMCWEB_ROUTE(app,
2416 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002417 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002418 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002419 [&app](const crow::Request& req,
2420 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2421 const std::string& param) {
2422 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2423 {
2424 return;
2425 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002426 deleteDumpEntry(asyncResp, param, "bmc");
2427 });
2428}
2429
2430inline void requestRoutesBMCDumpCreate(App& app)
2431{
George Liu0fda0f12021-11-16 10:06:17 +08002432 BMCWEB_ROUTE(
2433 app,
2434 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002435 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002436 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002437 [&app](const crow::Request& req,
2438 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2439 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2440 {
2441 return;
2442 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002443 createDump(asyncResp, req, "BMC");
2444 });
2445}
2446
2447inline void requestRoutesBMCDumpClear(App& app)
2448{
George Liu0fda0f12021-11-16 10:06:17 +08002449 BMCWEB_ROUTE(
2450 app,
2451 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002452 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002453 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002454 [&app](const crow::Request& req,
2455 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2456 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2457 {
2458 return;
2459 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002460 clearDump(asyncResp, "BMC");
2461 });
2462}
2463
2464inline void requestRoutesSystemDumpService(App& app)
2465{
2466 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002467 .privileges(redfish::privileges::getLogService)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002468 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
2469 const std::shared_ptr<
2470 bmcweb::AsyncResp>&
2471 asyncResp) {
2472 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002473 {
Ed Tanous45ca1b82022-03-25 13:07:27 -07002474 return;
2475 }
2476 asyncResp->res.jsonValue["@odata.id"] =
2477 "/redfish/v1/Systems/system/LogServices/Dump";
2478 asyncResp->res.jsonValue["@odata.type"] =
2479 "#LogService.v1_2_0.LogService";
2480 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2481 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2482 asyncResp->res.jsonValue["Id"] = "Dump";
2483 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302484
Ed Tanous45ca1b82022-03-25 13:07:27 -07002485 std::pair<std::string, std::string> redfishDateTimeOffset =
2486 crow::utility::getDateTimeOffsetNow();
2487 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2488 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2489 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302490
Ed Tanous14766872022-03-15 10:44:42 -07002491 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2492 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2493 asyncResp->res
2494 .jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2495 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
2496
2497 asyncResp->res
2498 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2499 ["target"] =
2500 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002501 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002502}
2503
2504inline void requestRoutesSystemDumpEntryCollection(App& app)
2505{
2506
2507 /**
2508 * Functions triggers appropriate requests on DBus
2509 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002510 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002511 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002512 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002513 [&app](const crow::Request& req,
2514 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2515 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2516 {
2517 return;
2518 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002519 asyncResp->res.jsonValue["@odata.type"] =
2520 "#LogEntryCollection.LogEntryCollection";
2521 asyncResp->res.jsonValue["@odata.id"] =
2522 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2523 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2524 asyncResp->res.jsonValue["Description"] =
2525 "Collection of System Dump Entries";
2526
2527 getDumpEntryCollection(asyncResp, "System");
2528 });
2529}
2530
2531inline void requestRoutesSystemDumpEntry(App& app)
2532{
2533 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002534 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002535 .privileges(redfish::privileges::getLogEntry)
2536
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002537 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002538 [&app](const crow::Request& req,
2539 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2540 const std::string& param) {
2541 getDumpEntryById(app, req, asyncResp, param, "System");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002542 });
2543
2544 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002545 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002546 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002547 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002548 [&app](const crow::Request& req,
2549 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2550 const std::string& param) {
2551 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2552 {
2553 return;
2554 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002555 deleteDumpEntry(asyncResp, param, "system");
2556 });
2557}
2558
2559inline void requestRoutesSystemDumpCreate(App& app)
2560{
George Liu0fda0f12021-11-16 10:06:17 +08002561 BMCWEB_ROUTE(
2562 app,
2563 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002564 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002565 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002566 [&app](const crow::Request& req,
2567 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2568 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2569 {
2570 return;
2571 }
2572 createDump(asyncResp, req, "System");
2573 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002574}
2575
2576inline void requestRoutesSystemDumpClear(App& app)
2577{
George Liu0fda0f12021-11-16 10:06:17 +08002578 BMCWEB_ROUTE(
2579 app,
2580 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002581 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002582 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002583 [&app](const crow::Request& req,
2584 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002585
Ed Tanous45ca1b82022-03-25 13:07:27 -07002586 {
2587 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2588 {
2589 return;
2590 }
2591 clearDump(asyncResp, "System");
2592 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002593}
2594
2595inline void requestRoutesCrashdumpService(App& app)
2596{
2597 // Note: Deviated from redfish privilege registry for GET & HEAD
2598 // method for security reasons.
2599 /**
2600 * Functions triggers appropriate requests on DBus
2601 */
2602 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002603 // This is incorrect, should be:
2604 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002605 .privileges({{"ConfigureManager"}})
Ed Tanous45ca1b82022-03-25 13:07:27 -07002606 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
2607 const std::shared_ptr<
2608 bmcweb::AsyncResp>&
2609 asyncResp) {
2610 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2611 {
2612 return;
2613 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002614 // Copy over the static data to include the entries added by
2615 // SubRoute
2616 asyncResp->res.jsonValue["@odata.id"] =
2617 "/redfish/v1/Systems/system/LogServices/Crashdump";
2618 asyncResp->res.jsonValue["@odata.type"] =
2619 "#LogService.v1_2_0.LogService";
2620 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2621 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2622 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2623 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2624 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302625
2626 std::pair<std::string, std::string> redfishDateTimeOffset =
2627 crow::utility::getDateTimeOffsetNow();
2628 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2629 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2630 redfishDateTimeOffset.second;
2631
Ed Tanous14766872022-03-15 10:44:42 -07002632 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2633 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2634 asyncResp->res
2635 .jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2636 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2637 asyncResp->res
2638 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2639 ["target"] =
2640 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002641 });
2642}
2643
2644void inline requestRoutesCrashdumpClear(App& app)
2645{
George Liu0fda0f12021-11-16 10:06:17 +08002646 BMCWEB_ROUTE(
2647 app,
2648 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002649 // This is incorrect, should be:
2650 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002651 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002652 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002653 [&app](const crow::Request& req,
2654 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2655 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2656 {
2657 return;
2658 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002659 crow::connections::systemBus->async_method_call(
2660 [asyncResp](const boost::system::error_code ec,
2661 const std::string&) {
2662 if (ec)
2663 {
2664 messages::internalError(asyncResp->res);
2665 return;
2666 }
2667 messages::success(asyncResp->res);
2668 },
2669 crashdumpObject, crashdumpPath, deleteAllInterface,
2670 "DeleteAll");
2671 });
2672}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002673
zhanghch058d1b46d2021-04-01 11:18:24 +08002674static void
2675 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2676 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002677{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002678 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002679 [asyncResp, logID,
2680 &logEntryJson](const boost::system::error_code ec,
2681 const dbus::utility::DBusPropertiesMap& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002682 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002683 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002684 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2685 if (ec.value() ==
2686 boost::system::linux_error::bad_request_descriptor)
2687 {
2688 messages::resourceNotFound(asyncResp->res, "LogEntry",
2689 logID);
2690 }
2691 else
2692 {
2693 messages::internalError(asyncResp->res);
2694 }
2695 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002696 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002697
Johnathan Mantey043a0532020-03-10 17:15:28 -07002698 std::string timestamp{};
2699 std::string filename{};
2700 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002701 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002702
2703 if (filename.empty() || timestamp.empty())
2704 {
Ed Tanousace85d62021-10-26 12:45:59 -07002705 messages::resourceMissingAtURI(
2706 asyncResp->res, crow::utility::urlFromPieces(logID));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002707 return;
2708 }
2709
2710 std::string crashdumpURI =
2711 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2712 logID + "/" + filename;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002713 nlohmann::json logEntry = {
Jason M. Bills4978b632022-02-22 14:17:43 -08002714 {"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
2715 {"@odata.id",
2716 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2717 logID},
2718 {"Name", "CPU Crashdump"},
2719 {"Id", logID},
2720 {"EntryType", "Oem"},
2721 {"AdditionalDataURI", std::move(crashdumpURI)},
2722 {"DiagnosticDataType", "OEM"},
2723 {"OEMDiagnosticDataType", "PECICrashdump"},
2724 {"Created", std::move(timestamp)}};
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002725
2726 // If logEntryJson references an array of LogEntry resources
2727 // ('Members' list), then push this as a new entry, otherwise set it
2728 // directly
2729 if (logEntryJson.is_array())
2730 {
2731 logEntryJson.push_back(logEntry);
2732 asyncResp->res.jsonValue["Members@odata.count"] =
2733 logEntryJson.size();
2734 }
2735 else
2736 {
2737 logEntryJson = logEntry;
2738 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002739 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002740 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002741 std::move(getStoredLogCallback), crashdumpObject,
2742 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002743 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002744}
2745
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002746inline void requestRoutesCrashdumpEntryCollection(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 /**
2751 * Functions triggers appropriate requests on DBus
2752 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002753 BMCWEB_ROUTE(app,
2754 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002755 // This is incorrect, should be.
2756 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002757 .privileges({{"ConfigureComponents"}})
Ed Tanous45ca1b82022-03-25 13:07:27 -07002758 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
2759 const std::shared_ptr<
2760 bmcweb::AsyncResp>&
2761 asyncResp) {
2762 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2763 {
2764 return;
2765 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002766 crow::connections::systemBus->async_method_call(
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002767 [asyncResp](const boost::system::error_code ec,
2768 const std::vector<std::string>& resp) {
2769 if (ec)
2770 {
2771 if (ec.value() !=
2772 boost::system::errc::no_such_file_or_directory)
2773 {
2774 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2775 << ec.message();
2776 messages::internalError(asyncResp->res);
2777 return;
2778 }
2779 }
2780 asyncResp->res.jsonValue["@odata.type"] =
2781 "#LogEntryCollection.LogEntryCollection";
2782 asyncResp->res.jsonValue["@odata.id"] =
2783 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2784 asyncResp->res.jsonValue["Name"] =
2785 "Open BMC Crashdump Entries";
2786 asyncResp->res.jsonValue["Description"] =
2787 "Collection of Crashdump Entries";
2788 asyncResp->res.jsonValue["Members"] =
2789 nlohmann::json::array();
Brandon Kima2dd60a2022-03-14 11:42:34 -07002790 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002791
2792 for (const std::string& path : resp)
2793 {
2794 const sdbusplus::message::object_path objPath(path);
2795 // Get the log ID
2796 std::string logID = objPath.filename();
2797 if (logID.empty())
2798 {
2799 continue;
2800 }
2801 // Add the log entry to the array
2802 logCrashdumpEntry(asyncResp, logID,
2803 asyncResp->res.jsonValue["Members"]);
2804 }
2805 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002806 "xyz.openbmc_project.ObjectMapper",
2807 "/xyz/openbmc_project/object_mapper",
2808 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2809 std::array<const char*, 1>{crashdumpInterface});
2810 });
2811}
Ed Tanous1da66f72018-07-27 16:13:37 -07002812
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002813inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002814{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002815 // Note: Deviated from redfish privilege registry for GET & HEAD
2816 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002817
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002818 BMCWEB_ROUTE(
2819 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002820 // this is incorrect, should be
2821 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002822 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002823 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002824 [&app](const crow::Request& req,
2825 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2826 const std::string& param) {
2827 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2828 {
2829 return;
2830 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002831 const std::string& logID = param;
2832 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2833 });
2834}
Ed Tanous1da66f72018-07-27 16:13:37 -07002835
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002836inline void requestRoutesCrashdumpFile(App& app)
2837{
2838 // Note: Deviated from redfish privilege registry for GET & HEAD
2839 // method for security reasons.
2840 BMCWEB_ROUTE(
2841 app,
2842 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002843 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002844 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002845 [&app](const crow::Request& req,
2846 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2847 const std::string& logID, const std::string& fileName) {
2848 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2849 {
2850 return;
2851 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002852 auto getStoredLogCallback =
Ed Tanousace85d62021-10-26 12:45:59 -07002853 [asyncResp, logID, fileName,
2854 url(boost::urls::url(req.urlView))](
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002855 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002856 const std::vector<std::pair<
2857 std::string, dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002858 resp) {
2859 if (ec)
2860 {
2861 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2862 << ec.message();
2863 messages::internalError(asyncResp->res);
2864 return;
2865 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002866
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002867 std::string dbusFilename{};
2868 std::string dbusTimestamp{};
2869 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002870
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002871 parseCrashdumpParameters(resp, dbusFilename,
2872 dbusTimestamp, dbusFilepath);
2873
2874 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2875 dbusFilepath.empty())
2876 {
Ed Tanousace85d62021-10-26 12:45:59 -07002877 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002878 return;
2879 }
2880
2881 // Verify the file name parameter is correct
2882 if (fileName != dbusFilename)
2883 {
Ed Tanousace85d62021-10-26 12:45:59 -07002884 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002885 return;
2886 }
2887
2888 if (!std::filesystem::exists(dbusFilepath))
2889 {
Ed Tanousace85d62021-10-26 12:45:59 -07002890 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002891 return;
2892 }
Jason M. Bills2d314912022-01-12 13:59:01 -08002893 std::ifstream ifs(dbusFilepath,
2894 std::ios::in | std::ios::binary);
2895 asyncResp->res.body() = std::string(
2896 std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002897
2898 // Configure this to be a file download when accessed
2899 // from a browser
2900 asyncResp->res.addHeader("Content-Disposition",
2901 "attachment");
2902 };
2903 crow::connections::systemBus->async_method_call(
2904 std::move(getStoredLogCallback), crashdumpObject,
2905 crashdumpPath + std::string("/") + logID,
2906 "org.freedesktop.DBus.Properties", "GetAll",
2907 crashdumpInterface);
2908 });
2909}
2910
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002911enum class OEMDiagnosticType
2912{
2913 onDemand,
2914 telemetry,
2915 invalid,
2916};
2917
Ed Tanousf7725d72022-03-07 12:46:00 -08002918inline OEMDiagnosticType
2919 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002920{
2921 if (oemDiagStr == "OnDemand")
2922 {
2923 return OEMDiagnosticType::onDemand;
2924 }
2925 if (oemDiagStr == "Telemetry")
2926 {
2927 return OEMDiagnosticType::telemetry;
2928 }
2929
2930 return OEMDiagnosticType::invalid;
2931}
2932
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002933inline void requestRoutesCrashdumpCollect(App& app)
2934{
2935 // Note: Deviated from redfish privilege registry for GET & HEAD
2936 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002937 BMCWEB_ROUTE(
2938 app,
2939 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002940 // The below is incorrect; Should be ConfigureManager
2941 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002942 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002943 .methods(
2944 boost::beast::http::verb::
Ed Tanous45ca1b82022-03-25 13:07:27 -07002945 post)([&app](
2946 const crow::Request& req,
2947 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2948 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2949 {
2950 return;
2951 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002952 std::string diagnosticDataType;
2953 std::string oemDiagnosticDataType;
Willy Tu15ed6782021-12-14 11:03:16 -08002954 if (!redfish::json_util::readJsonAction(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002955 req, asyncResp->res, "DiagnosticDataType",
2956 diagnosticDataType, "OEMDiagnosticDataType",
2957 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002958 {
James Feist46229572020-02-19 15:11:58 -08002959 return;
2960 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002961
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002962 if (diagnosticDataType != "OEM")
2963 {
2964 BMCWEB_LOG_ERROR
2965 << "Only OEM DiagnosticDataType supported for Crashdump";
2966 messages::actionParameterValueFormatError(
2967 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2968 "CollectDiagnosticData");
2969 return;
2970 }
2971
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002972 OEMDiagnosticType oemDiagType =
2973 getOEMDiagnosticType(oemDiagnosticDataType);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002974
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002975 std::string iface;
2976 std::string method;
2977 std::string taskMatchStr;
2978 if (oemDiagType == OEMDiagnosticType::onDemand)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002979 {
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002980 iface = crashdumpOnDemandInterface;
2981 method = "GenerateOnDemandLog";
2982 taskMatchStr = "type='signal',"
2983 "interface='org.freedesktop.DBus.Properties',"
2984 "member='PropertiesChanged',"
2985 "arg0namespace='com.intel.crashdump'";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002986 }
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002987 else if (oemDiagType == OEMDiagnosticType::telemetry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002988 {
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002989 iface = crashdumpTelemetryInterface;
2990 method = "GenerateTelemetryLog";
2991 taskMatchStr = "type='signal',"
2992 "interface='org.freedesktop.DBus.Properties',"
2993 "member='PropertiesChanged',"
2994 "arg0namespace='com.intel.crashdump'";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002995 }
2996 else
2997 {
2998 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2999 << oemDiagnosticDataType;
3000 messages::actionParameterValueFormatError(
3001 asyncResp->res, oemDiagnosticDataType,
3002 "OEMDiagnosticDataType", "CollectDiagnosticData");
3003 return;
3004 }
Jason M. Billsc5a4c822022-01-06 15:51:23 -08003005
3006 auto collectCrashdumpCallback =
3007 [asyncResp, payload(task::Payload(req)),
3008 taskMatchStr](const boost::system::error_code ec,
3009 const std::string&) mutable {
3010 if (ec)
3011 {
3012 if (ec.value() ==
3013 boost::system::errc::operation_not_supported)
3014 {
3015 messages::resourceInStandby(asyncResp->res);
3016 }
3017 else if (ec.value() ==
3018 boost::system::errc::device_or_resource_busy)
3019 {
3020 messages::serviceTemporarilyUnavailable(
3021 asyncResp->res, "60");
3022 }
3023 else
3024 {
3025 messages::internalError(asyncResp->res);
3026 }
3027 return;
3028 }
3029 std::shared_ptr<task::TaskData> task =
3030 task::TaskData::createTask(
3031 [](boost::system::error_code err,
3032 sdbusplus::message::message&,
3033 const std::shared_ptr<task::TaskData>&
3034 taskData) {
3035 if (!err)
3036 {
3037 taskData->messages.emplace_back(
3038 messages::taskCompletedOK(
3039 std::to_string(taskData->index)));
3040 taskData->state = "Completed";
3041 }
3042 return task::completed;
3043 },
3044 taskMatchStr);
3045
3046 task->startTimer(std::chrono::minutes(5));
3047 task->populateResp(asyncResp->res);
3048 task->payload.emplace(std::move(payload));
3049 };
3050
3051 crow::connections::systemBus->async_method_call(
3052 std::move(collectCrashdumpCallback), crashdumpObject,
3053 crashdumpPath, iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003054 });
3055}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003056
Andrew Geisslercb92c032018-08-17 07:56:14 -07003057/**
3058 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3059 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003060inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003061{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003062 /**
3063 * Function handles POST method request.
3064 * The Clear Log actions does not require any parameter.The action deletes
3065 * all entries found in the Entries collection for this Log Service.
3066 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003067
George Liu0fda0f12021-11-16 10:06:17 +08003068 BMCWEB_ROUTE(
3069 app,
3070 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003071 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003072 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003073 [&app](const crow::Request& req,
3074 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3075 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3076 {
3077 return;
3078 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003079 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003080
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003081 // Process response from Logging service.
3082 auto respHandler = [asyncResp](
3083 const boost::system::error_code ec) {
3084 BMCWEB_LOG_DEBUG
3085 << "doClearLog resp_handler callback: Done";
3086 if (ec)
3087 {
3088 // TODO Handle for specific error code
3089 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
3090 << ec;
3091 asyncResp->res.result(
3092 boost::beast::http::status::internal_server_error);
3093 return;
3094 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003095
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003096 asyncResp->res.result(
3097 boost::beast::http::status::no_content);
3098 };
3099
3100 // Make call to Logging service to request Clear Log
3101 crow::connections::systemBus->async_method_call(
3102 respHandler, "xyz.openbmc_project.Logging",
3103 "/xyz/openbmc_project/logging",
3104 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3105 });
3106}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003107
3108/****************************************************
3109 * Redfish PostCode interfaces
3110 * using DBUS interface: getPostCodesTS
3111 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003112inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003113{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003114 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003115 .privileges(redfish::privileges::getLogService)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003116 .methods(boost::beast::http::verb::get)([&app](const crow::Request& req,
3117 const std::shared_ptr<
3118 bmcweb::AsyncResp>&
3119 asyncResp) {
3120 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3121 {
3122 return;
3123 }
Ed Tanous14766872022-03-15 10:44:42 -07003124
3125 asyncResp->res.jsonValue["@odata.id"] =
3126 "/redfish/v1/Systems/system/LogServices/PostCodes";
3127 asyncResp->res.jsonValue["@odata.type"] =
3128 "#LogService.v1_1_0.LogService";
3129 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3130 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3131 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3132 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3133 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3134 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303135
George Liu0fda0f12021-11-16 10:06:17 +08003136 std::pair<std::string, std::string> redfishDateTimeOffset =
3137 crow::utility::getDateTimeOffsetNow();
3138 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3139 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3140 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303141
George Liu0fda0f12021-11-16 10:06:17 +08003142 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3143 {"target",
3144 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3145 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003146}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003147
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003148inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003149{
George Liu0fda0f12021-11-16 10:06:17 +08003150 BMCWEB_ROUTE(
3151 app,
3152 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003153 // The following privilege is incorrect; It should be ConfigureManager
3154 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003155 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003156 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003157 [&app](const crow::Request& req,
3158 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3159 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3160 {
3161 return;
3162 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003163 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003164
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003165 // Make call to post-code service to request clear all
3166 crow::connections::systemBus->async_method_call(
3167 [asyncResp](const boost::system::error_code ec) {
3168 if (ec)
3169 {
3170 // TODO Handle for specific error code
3171 BMCWEB_LOG_ERROR
3172 << "doClearPostCodes resp_handler got error "
3173 << ec;
3174 asyncResp->res.result(boost::beast::http::status::
3175 internal_server_error);
3176 messages::internalError(asyncResp->res);
3177 return;
3178 }
3179 },
3180 "xyz.openbmc_project.State.Boot.PostCode0",
3181 "/xyz/openbmc_project/State/Boot/PostCode0",
3182 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3183 });
3184}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003185
3186static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003187 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303188 const boost::container::flat_map<
3189 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003190 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3191 const uint64_t skip = 0, const uint64_t top = 0)
3192{
3193 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003194 const registries::Message* message =
3195 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003196
3197 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003198 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003199
3200 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303201 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3202 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003203 {
3204 currentCodeIndex++;
3205 std::string postcodeEntryID =
3206 "B" + std::to_string(bootIndex) + "-" +
3207 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3208
3209 uint64_t usecSinceEpoch = code.first;
3210 uint64_t usTimeOffset = 0;
3211
3212 if (1 == currentCodeIndex)
3213 { // already incremented
3214 firstCodeTimeUs = code.first;
3215 }
3216 else
3217 {
3218 usTimeOffset = code.first - firstCodeTimeUs;
3219 }
3220
3221 // skip if no specific codeIndex is specified and currentCodeIndex does
3222 // not fall between top and skip
3223 if ((codeIndex == 0) &&
3224 (currentCodeIndex <= skip || currentCodeIndex > top))
3225 {
3226 continue;
3227 }
3228
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003229 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003230 // currentIndex
3231 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3232 {
3233 // This is done for simplicity. 1st entry is needed to calculate
3234 // time offset. To improve efficiency, one can get to the entry
3235 // directly (possibly with flatmap's nth method)
3236 continue;
3237 }
3238
3239 // currentCodeIndex is within top and skip or equal to specified code
3240 // index
3241
3242 // Get the Created time from the timestamp
3243 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003244 entryTimeStr =
3245 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003246
3247 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3248 std::ostringstream hexCode;
3249 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303250 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003251 std::ostringstream timeOffsetStr;
3252 // Set Fixed -Point Notation
3253 timeOffsetStr << std::fixed;
3254 // Set precision to 4 digits
3255 timeOffsetStr << std::setprecision(4);
3256 // Add double to stream
3257 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3258 std::vector<std::string> messageArgs = {
3259 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3260
3261 // Get MessageArgs template from message registry
3262 std::string msg;
3263 if (message != nullptr)
3264 {
3265 msg = message->message;
3266
3267 // fill in this post code value
3268 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003269 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003270 {
3271 std::string argStr = "%" + std::to_string(++i);
3272 size_t argPos = msg.find(argStr);
3273 if (argPos != std::string::npos)
3274 {
3275 msg.replace(argPos, argStr.length(), messageArg);
3276 }
3277 }
3278 }
3279
Tim Leed4342a92020-04-27 11:47:58 +08003280 // Get Severity template from message registry
3281 std::string severity;
3282 if (message != nullptr)
3283 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003284 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003285 }
3286
ZhikuiRena3316fc2020-01-29 14:58:08 -08003287 // add to AsyncResp
3288 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003289 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003290 bmcLogEntry = {
3291 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3292 {"@odata.id",
3293 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3294 postcodeEntryID},
3295 {"Name", "POST Code Log Entry"},
3296 {"Id", postcodeEntryID},
3297 {"Message", std::move(msg)},
3298 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3299 {"MessageArgs", std::move(messageArgs)},
3300 {"EntryType", "Event"},
3301 {"Severity", std::move(severity)},
3302 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003303 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3304 {
3305 bmcLogEntry["AdditionalDataURI"] =
3306 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3307 postcodeEntryID + "/attachment";
3308 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003309 }
3310}
3311
zhanghch058d1b46d2021-04-01 11:18:24 +08003312static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003313 const uint16_t bootIndex,
3314 const uint64_t codeIndex)
3315{
3316 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303317 [aResp, bootIndex,
3318 codeIndex](const boost::system::error_code ec,
3319 const boost::container::flat_map<
3320 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3321 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003322 if (ec)
3323 {
3324 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3325 messages::internalError(aResp->res);
3326 return;
3327 }
3328
3329 // skip the empty postcode boots
3330 if (postcode.empty())
3331 {
3332 return;
3333 }
3334
3335 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3336
3337 aResp->res.jsonValue["Members@odata.count"] =
3338 aResp->res.jsonValue["Members"].size();
3339 },
Jonathan Doman15124762021-01-07 17:54:17 -08003340 "xyz.openbmc_project.State.Boot.PostCode0",
3341 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003342 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3343 bootIndex);
3344}
3345
zhanghch058d1b46d2021-04-01 11:18:24 +08003346static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003347 const uint16_t bootIndex,
3348 const uint16_t bootCount,
3349 const uint64_t entryCount, const uint64_t skip,
3350 const uint64_t top)
3351{
3352 crow::connections::systemBus->async_method_call(
3353 [aResp, bootIndex, bootCount, entryCount, skip,
3354 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303355 const boost::container::flat_map<
3356 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3357 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003358 if (ec)
3359 {
3360 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3361 messages::internalError(aResp->res);
3362 return;
3363 }
3364
3365 uint64_t endCount = entryCount;
3366 if (!postcode.empty())
3367 {
3368 endCount = entryCount + postcode.size();
3369
3370 if ((skip < endCount) && ((top + skip) > entryCount))
3371 {
3372 uint64_t thisBootSkip =
3373 std::max(skip, entryCount) - entryCount;
3374 uint64_t thisBootTop =
3375 std::min(top + skip, endCount) - entryCount;
3376
3377 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3378 thisBootSkip, thisBootTop);
3379 }
3380 aResp->res.jsonValue["Members@odata.count"] = endCount;
3381 }
3382
3383 // continue to previous bootIndex
3384 if (bootIndex < bootCount)
3385 {
3386 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3387 bootCount, endCount, skip, top);
3388 }
3389 else
3390 {
3391 aResp->res.jsonValue["Members@odata.nextLink"] =
George Liu0fda0f12021-11-16 10:06:17 +08003392 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
ZhikuiRena3316fc2020-01-29 14:58:08 -08003393 std::to_string(skip + top);
3394 }
3395 },
Jonathan Doman15124762021-01-07 17:54:17 -08003396 "xyz.openbmc_project.State.Boot.PostCode0",
3397 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003398 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3399 bootIndex);
3400}
3401
zhanghch058d1b46d2021-04-01 11:18:24 +08003402static void
3403 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3404 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003405{
3406 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003407 sdbusplus::asio::getProperty<uint16_t>(
3408 *crow::connections::systemBus,
3409 "xyz.openbmc_project.State.Boot.PostCode0",
3410 "/xyz/openbmc_project/State/Boot/PostCode0",
3411 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3412 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3413 const uint16_t bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003414 if (ec)
3415 {
3416 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3417 messages::internalError(aResp->res);
3418 return;
3419 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003420 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3421 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003422}
3423
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003424inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003425{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003426 BMCWEB_ROUTE(app,
3427 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003428 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003429 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003430 [&app](const crow::Request& req,
3431 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanousc937d2b2022-04-05 09:58:00 -07003432 query_param::QueryCapabilities capabilities = {
3433 .canDelegateTop = true,
3434 .canDelegateSkip = true,
3435 };
3436 query_param::Query delegatedQuery;
3437 if (!redfish::setUpRedfishRouteWithDelegation(
3438 app, req, asyncResp->res, delegatedQuery, capabilities))
Ed Tanous45ca1b82022-03-25 13:07:27 -07003439 {
3440 return;
3441 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003442 asyncResp->res.jsonValue["@odata.type"] =
3443 "#LogEntryCollection.LogEntryCollection";
3444 asyncResp->res.jsonValue["@odata.id"] =
3445 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3446 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3447 asyncResp->res.jsonValue["Description"] =
3448 "Collection of POST Code Log Entries";
3449 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3450 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003451
Ed Tanousc937d2b2022-04-05 09:58:00 -07003452 getCurrentBootNumber(asyncResp, delegatedQuery.skip,
3453 delegatedQuery.top);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003454 });
3455}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003456
George Liu647b3cd2021-07-05 12:43:56 +08003457/**
3458 * @brief Parse post code ID and get the current value and index value
3459 * eg: postCodeID=B1-2, currentValue=1, index=2
3460 *
3461 * @param[in] postCodeID Post Code ID
3462 * @param[out] currentValue Current value
3463 * @param[out] index Index value
3464 *
3465 * @return bool true if the parsing is successful, false the parsing fails
3466 */
3467inline static bool parsePostCode(const std::string& postCodeID,
3468 uint64_t& currentValue, uint16_t& index)
3469{
3470 std::vector<std::string> split;
3471 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3472 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3473 {
3474 return false;
3475 }
3476
Ed Tanousca45aa32022-01-07 09:28:45 -08003477 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003478 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003479 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003480 const char* end = split[0].data() + split[0].size();
3481 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3482
3483 if (ptrIndex != end || ecIndex != std::errc())
3484 {
3485 return false;
3486 }
3487
3488 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003489
3490 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003491 end = split[1].data() + split[1].size();
3492 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003493
Ed Tanousdcf2ebc2022-01-25 10:07:45 -08003494 return ptrValue == end && ecValue != std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003495}
3496
3497inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3498{
George Liu0fda0f12021-11-16 10:06:17 +08003499 BMCWEB_ROUTE(
3500 app,
3501 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003502 .privileges(redfish::privileges::getLogEntry)
3503 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003504 [&app](const crow::Request& req,
3505 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3506 const std::string& postCodeID) {
3507 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3508 {
3509 return;
3510 }
George Liu647b3cd2021-07-05 12:43:56 +08003511 if (!http_helpers::isOctetAccepted(
3512 req.getHeaderValue("Accept")))
3513 {
3514 asyncResp->res.result(
3515 boost::beast::http::status::bad_request);
3516 return;
3517 }
3518
3519 uint64_t currentValue = 0;
3520 uint16_t index = 0;
3521 if (!parsePostCode(postCodeID, currentValue, index))
3522 {
3523 messages::resourceNotFound(asyncResp->res, "LogEntry",
3524 postCodeID);
3525 return;
3526 }
3527
3528 crow::connections::systemBus->async_method_call(
3529 [asyncResp, postCodeID, currentValue](
3530 const boost::system::error_code ec,
3531 const std::vector<std::tuple<
3532 uint64_t, std::vector<uint8_t>>>& postcodes) {
3533 if (ec.value() == EBADR)
3534 {
3535 messages::resourceNotFound(asyncResp->res,
3536 "LogEntry", postCodeID);
3537 return;
3538 }
3539 if (ec)
3540 {
3541 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3542 messages::internalError(asyncResp->res);
3543 return;
3544 }
3545
3546 size_t value = static_cast<size_t>(currentValue) - 1;
3547 if (value == std::string::npos ||
3548 postcodes.size() < currentValue)
3549 {
3550 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3551 messages::resourceNotFound(asyncResp->res,
3552 "LogEntry", postCodeID);
3553 return;
3554 }
3555
Ed Tanous9eb808c2022-01-25 10:19:23 -08003556 const auto& [tID, c] = postcodes[value];
Ed Tanous46ff87b2022-01-07 09:25:51 -08003557 if (c.empty())
George Liu647b3cd2021-07-05 12:43:56 +08003558 {
3559 BMCWEB_LOG_INFO << "No found post code data";
3560 messages::resourceNotFound(asyncResp->res,
3561 "LogEntry", postCodeID);
3562 return;
3563 }
Ed Tanous46ff87b2022-01-07 09:25:51 -08003564 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3565 const char* d = reinterpret_cast<const char*>(c.data());
3566 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003567
3568 asyncResp->res.addHeader("Content-Type",
3569 "application/octet-stream");
3570 asyncResp->res.addHeader("Content-Transfer-Encoding",
3571 "Base64");
3572 asyncResp->res.body() =
3573 crow::utility::base64encode(strData);
3574 },
3575 "xyz.openbmc_project.State.Boot.PostCode0",
3576 "/xyz/openbmc_project/State/Boot/PostCode0",
3577 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3578 index);
3579 });
3580}
3581
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003582inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003583{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003584 BMCWEB_ROUTE(
3585 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003586 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003587 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003588 [&app](const crow::Request& req,
3589 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3590 const std::string& targetID) {
3591 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3592 {
3593 return;
3594 }
George Liu647b3cd2021-07-05 12:43:56 +08003595 uint16_t bootIndex = 0;
3596 uint64_t codeIndex = 0;
3597 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003598 {
3599 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07003600 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003601 return;
3602 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003603 if (bootIndex == 0 || codeIndex == 0)
3604 {
3605 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3606 << targetID;
3607 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003608
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003609 asyncResp->res.jsonValue["@odata.type"] =
3610 "#LogEntry.v1_4_0.LogEntry";
3611 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08003612 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003613 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3614 asyncResp->res.jsonValue["Description"] =
3615 "Collection of POST Code Log Entries";
3616 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3617 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003618
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003619 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3620 });
3621}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003622
Ed Tanous1da66f72018-07-27 16:13:37 -07003623} // namespace redfish