blob: 3572ccaf65e651f96a849116be1047d9f54d0d35 [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 Tanous002d39b2022-05-31 08:59:27 -070066 std::span<const MessageEntry>::iterator messageIt =
67 std::find_if(registry.begin(), registry.end(),
68 [&messageKey](const MessageEntry& messageEntry) {
69 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) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700352 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();
361 std::string dumpEntryPath =
362 "/xyz/openbmc_project/dump/" +
363 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
364
365 std::sort(resp.begin(), resp.end(), [](const auto& l, const auto& r) {
366 return AlphanumLess<std::string>()(l.first.filename(),
367 r.first.filename());
368 });
369
370 for (auto& object : resp)
371 {
372 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500373 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700374 continue;
375 }
376 uint64_t timestamp = 0;
377 uint64_t size = 0;
378 std::string dumpStatus;
379 nlohmann::json thisEntry;
380
381 std::string entryID = object.first.filename();
382 if (entryID.empty())
383 {
384 continue;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500385 }
386
Ed Tanous002d39b2022-05-31 08:59:27 -0700387 for (auto& interfaceMap : object.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500388 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700389 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500390 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700391 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500392 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700393 if (propertyMap.first == "Status")
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500394 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700395 const auto* status =
396 std::get_if<std::string>(&propertyMap.second);
397 if (status == nullptr)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500398 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700399 messages::internalError(asyncResp->res);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500400 break;
401 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700402 dumpStatus = *status;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500403 }
404 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700405 }
406 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
407 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500408
Ed Tanous002d39b2022-05-31 08:59:27 -0700409 for (auto& propertyMap : interfaceMap.second)
410 {
411 if (propertyMap.first == "Size")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500412 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700413 const auto* sizePtr =
414 std::get_if<uint64_t>(&propertyMap.second);
415 if (sizePtr == nullptr)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500416 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700417 messages::internalError(asyncResp->res);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500418 break;
419 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700420 size = *sizePtr;
421 break;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500422 }
423 }
424 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700425 else if (interfaceMap.first ==
426 "xyz.openbmc_project.Time.EpochTime")
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500427 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500428
Ed Tanous002d39b2022-05-31 08:59:27 -0700429 for (const auto& propertyMap : interfaceMap.second)
430 {
431 if (propertyMap.first == "Elapsed")
432 {
433 const uint64_t* usecsTimeStamp =
434 std::get_if<uint64_t>(&propertyMap.second);
435 if (usecsTimeStamp == nullptr)
436 {
437 messages::internalError(asyncResp->res);
438 break;
439 }
440 timestamp = (*usecsTimeStamp / 1000 / 1000);
441 break;
442 }
443 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500444 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500445 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700446
447 if (dumpStatus !=
448 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
449 !dumpStatus.empty())
450 {
451 // Dump status is not Complete, no need to enumerate
452 continue;
453 }
454
455 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
456 thisEntry["@odata.id"] = dumpPath + entryID;
457 thisEntry["Id"] = entryID;
458 thisEntry["EntryType"] = "Event";
459 thisEntry["Created"] = crow::utility::getDateTimeUint(timestamp);
460 thisEntry["Name"] = dumpType + " Dump Entry";
461
462 thisEntry["AdditionalDataSizeBytes"] = size;
463
464 if (dumpType == "BMC")
465 {
466 thisEntry["DiagnosticDataType"] = "Manager";
467 thisEntry["AdditionalDataURI"] =
468 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
469 entryID + "/attachment";
470 }
471 else if (dumpType == "System")
472 {
473 thisEntry["DiagnosticDataType"] = "OEM";
474 thisEntry["OEMDiagnosticDataType"] = "System";
475 thisEntry["AdditionalDataURI"] =
476 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
477 entryID + "/attachment";
478 }
479 entriesArray.push_back(std::move(thisEntry));
480 }
481 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500482 },
483 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
484 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
485}
486
zhanghch058d1b46d2021-04-01 11:18:24 +0800487inline void
Ed Tanous45ca1b82022-03-25 13:07:27 -0700488 getDumpEntryById(crow::App& app, const crow::Request& req,
489 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800490 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500491{
Ed Tanous45ca1b82022-03-25 13:07:27 -0700492 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
493 {
494 return;
495 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500496 std::string dumpPath;
497 if (dumpType == "BMC")
498 {
499 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
500 }
501 else if (dumpType == "System")
502 {
503 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
504 }
505 else
506 {
507 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
508 messages::internalError(asyncResp->res);
509 return;
510 }
511
512 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800513 [asyncResp, entryID, dumpPath,
514 dumpType](const boost::system::error_code ec,
515 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700516 if (ec)
517 {
518 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
519 messages::internalError(asyncResp->res);
520 return;
521 }
522
523 bool foundDumpEntry = false;
524 std::string dumpEntryPath =
525 "/xyz/openbmc_project/dump/" +
526 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
527
528 for (const auto& objectPath : resp)
529 {
530 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500531 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700532 continue;
533 }
534
535 foundDumpEntry = true;
536 uint64_t timestamp = 0;
537 uint64_t size = 0;
538 std::string dumpStatus;
539
540 for (const auto& interfaceMap : objectPath.second)
541 {
542 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
543 {
544 for (const auto& propertyMap : interfaceMap.second)
545 {
546 if (propertyMap.first == "Status")
547 {
548 const std::string* status =
549 std::get_if<std::string>(&propertyMap.second);
550 if (status == nullptr)
551 {
552 messages::internalError(asyncResp->res);
553 break;
554 }
555 dumpStatus = *status;
556 }
557 }
558 }
559 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
560 {
561 for (const auto& propertyMap : interfaceMap.second)
562 {
563 if (propertyMap.first == "Size")
564 {
565 const uint64_t* sizePtr =
566 std::get_if<uint64_t>(&propertyMap.second);
567 if (sizePtr == nullptr)
568 {
569 messages::internalError(asyncResp->res);
570 break;
571 }
572 size = *sizePtr;
573 break;
574 }
575 }
576 }
577 else if (interfaceMap.first ==
578 "xyz.openbmc_project.Time.EpochTime")
579 {
580 for (const auto& propertyMap : interfaceMap.second)
581 {
582 if (propertyMap.first == "Elapsed")
583 {
584 const uint64_t* usecsTimeStamp =
585 std::get_if<uint64_t>(&propertyMap.second);
586 if (usecsTimeStamp == nullptr)
587 {
588 messages::internalError(asyncResp->res);
589 break;
590 }
591 timestamp = *usecsTimeStamp / 1000 / 1000;
592 break;
593 }
594 }
595 }
596 }
597
598 if (dumpStatus !=
599 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
600 !dumpStatus.empty())
601 {
602 // Dump status is not Complete
603 // return not found until status is changed to Completed
604 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
605 entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500606 return;
607 }
608
Ed Tanous002d39b2022-05-31 08:59:27 -0700609 asyncResp->res.jsonValue["@odata.type"] =
610 "#LogEntry.v1_8_0.LogEntry";
611 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
612 asyncResp->res.jsonValue["Id"] = entryID;
613 asyncResp->res.jsonValue["EntryType"] = "Event";
614 asyncResp->res.jsonValue["Created"] =
615 crow::utility::getDateTimeUint(timestamp);
616 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500617
Ed Tanous002d39b2022-05-31 08:59:27 -0700618 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
619
620 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500621 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700622 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
623 asyncResp->res.jsonValue["AdditionalDataURI"] =
624 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
625 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500626 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700627 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500628 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700629 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
630 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
631 asyncResp->res.jsonValue["AdditionalDataURI"] =
632 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
633 entryID + "/attachment";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500634 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700635 }
636 if (!foundDumpEntry)
637 {
638 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
639 messages::internalError(asyncResp->res);
640 return;
641 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500642 },
643 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
644 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
645}
646
zhanghch058d1b46d2021-04-01 11:18:24 +0800647inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800648 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500649 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500650{
Ed Tanous002d39b2022-05-31 08:59:27 -0700651 auto respHandler =
652 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500653 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
654 if (ec)
655 {
George Liu3de8d8b2021-03-22 17:49:39 +0800656 if (ec.value() == EBADR)
657 {
658 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
659 return;
660 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500661 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
662 << ec;
663 messages::internalError(asyncResp->res);
664 return;
665 }
666 };
667 crow::connections::systemBus->async_method_call(
668 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500669 "/xyz/openbmc_project/dump/" +
670 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
671 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500672 "xyz.openbmc_project.Object.Delete", "Delete");
673}
674
zhanghch058d1b46d2021-04-01 11:18:24 +0800675inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700676 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800677 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
678 const uint32_t& dumpId, const std::string& dumpPath,
679 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500680{
681 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500682 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500683 boost::system::error_code err, sdbusplus::message::message& m,
684 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700685 if (err)
686 {
687 BMCWEB_LOG_ERROR << "Error in creating a dump";
688 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500689 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700690 }
691
692 dbus::utility::DBusInteracesMap interfacesList;
693
694 sdbusplus::message::object_path objPath;
695
696 m.read(objPath, interfacesList);
697
698 if (objPath.str ==
699 "/xyz/openbmc_project/dump/" +
700 std::string(boost::algorithm::to_lower_copy(dumpType)) +
701 "/entry/" + std::to_string(dumpId))
702 {
703 nlohmann::json retMessage = messages::success();
704 taskData->messages.emplace_back(retMessage);
705
706 std::string headerLoc =
707 "Location: " + dumpPath + std::to_string(dumpId);
708 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
709
710 taskData->state = "Completed";
711 return task::completed;
712 }
713 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500714 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800715 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500716 "member='InterfacesAdded', "
717 "path='/xyz/openbmc_project/dump'");
718
719 task->startTimer(std::chrono::minutes(3));
720 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700721 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500722}
723
zhanghch058d1b46d2021-04-01 11:18:24 +0800724inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
725 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500726{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500727 std::string dumpPath;
728 if (dumpType == "BMC")
729 {
730 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
731 }
732 else if (dumpType == "System")
733 {
734 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
735 }
736 else
737 {
738 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
739 messages::internalError(asyncResp->res);
740 return;
741 }
742
743 std::optional<std::string> diagnosticDataType;
744 std::optional<std::string> oemDiagnosticDataType;
745
Willy Tu15ed6782021-12-14 11:03:16 -0800746 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500747 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
748 "OEMDiagnosticDataType", oemDiagnosticDataType))
749 {
750 return;
751 }
752
753 if (dumpType == "System")
754 {
755 if (!oemDiagnosticDataType || !diagnosticDataType)
756 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800757 BMCWEB_LOG_ERROR
758 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500759 messages::actionParameterMissing(
760 asyncResp->res, "CollectDiagnosticData",
761 "DiagnosticDataType & OEMDiagnosticDataType");
762 return;
763 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700764 if ((*oemDiagnosticDataType != "System") ||
765 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500766 {
767 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700768 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500769 return;
770 }
771 }
772 else if (dumpType == "BMC")
773 {
774 if (!diagnosticDataType)
775 {
George Liu0fda0f12021-11-16 10:06:17 +0800776 BMCWEB_LOG_ERROR
777 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500778 messages::actionParameterMissing(
779 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
780 return;
781 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700782 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500783 {
784 BMCWEB_LOG_ERROR
785 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700786 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500787 return;
788 }
789 }
790
791 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700792 [asyncResp, payload(task::Payload(req)), dumpPath,
793 dumpType](const boost::system::error_code ec,
794 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700795 if (ec)
796 {
797 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
798 messages::internalError(asyncResp->res);
799 return;
800 }
801 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500802
Ed Tanous002d39b2022-05-31 08:59:27 -0700803 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
804 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500805 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500806 "xyz.openbmc_project.Dump.Manager",
807 "/xyz/openbmc_project/dump/" +
808 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500809 "xyz.openbmc_project.Dump.Create", "CreateDump");
810}
811
zhanghch058d1b46d2021-04-01 11:18:24 +0800812inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
813 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500814{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500815 std::string dumpTypeLowerCopy =
816 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800817
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500818 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800819 [asyncResp, dumpType](
820 const boost::system::error_code ec,
821 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700822 if (ec)
823 {
824 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
825 messages::internalError(asyncResp->res);
826 return;
827 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500828
Ed Tanous002d39b2022-05-31 08:59:27 -0700829 for (const std::string& path : subTreePaths)
830 {
831 sdbusplus::message::object_path objPath(path);
832 std::string logID = objPath.filename();
833 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500834 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700835 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500836 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700837 deleteDumpEntry(asyncResp, logID, dumpType);
838 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500839 },
840 "xyz.openbmc_project.ObjectMapper",
841 "/xyz/openbmc_project/object_mapper",
842 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500843 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
844 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
845 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500846}
847
Ed Tanousb9d36b42022-02-26 21:42:46 -0800848inline static void
849 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
850 std::string& filename, std::string& timestamp,
851 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700852{
853 for (auto property : params)
854 {
855 if (property.first == "Timestamp")
856 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500857 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500858 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700859 if (value != nullptr)
860 {
861 timestamp = *value;
862 }
863 }
864 else if (property.first == "Filename")
865 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500866 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500867 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700868 if (value != nullptr)
869 {
870 filename = *value;
871 }
872 }
873 else if (property.first == "Log")
874 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500875 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500876 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700877 if (value != nullptr)
878 {
879 logfile = *value;
880 }
881 }
882 }
883}
884
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500885constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700886inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700887{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800888 /**
889 * Functions triggers appropriate requests on DBus
890 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700891 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700892 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700893 .methods(boost::beast::http::verb::get)(
894 [&app](const crow::Request& req,
895 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
896 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
897 {
898 return;
899 }
900 // Collections don't include the static data added by SubRoute
901 // because it has a duplicate entry for members
902 asyncResp->res.jsonValue["@odata.type"] =
903 "#LogServiceCollection.LogServiceCollection";
904 asyncResp->res.jsonValue["@odata.id"] =
905 "/redfish/v1/Systems/system/LogServices";
906 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
907 asyncResp->res.jsonValue["Description"] =
908 "Collection of LogServices for this Computer System";
909 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
910 logServiceArray = nlohmann::json::array();
911 nlohmann::json::object_t eventLog;
912 eventLog["@odata.id"] =
913 "/redfish/v1/Systems/system/LogServices/EventLog";
914 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500915#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700916 nlohmann::json::object_t dumpLog;
917 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
918 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600919#endif
920
Jason M. Billsd53dd412019-02-12 17:16:22 -0800921#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700922 nlohmann::json::object_t crashdump;
923 crashdump["@odata.id"] =
924 "/redfish/v1/Systems/system/LogServices/Crashdump";
925 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800926#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800927
928#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700929 nlohmann::json::object_t hostlogger;
930 hostlogger["@odata.id"] =
931 "/redfish/v1/Systems/system/LogServices/HostLogger";
932 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800933#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700934 asyncResp->res.jsonValue["Members@odata.count"] =
935 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800936
Ed Tanous002d39b2022-05-31 08:59:27 -0700937 crow::connections::systemBus->async_method_call(
938 [asyncResp](const boost::system::error_code ec,
939 const dbus::utility::MapperGetSubTreePathsResponse&
940 subtreePath) {
941 if (ec)
942 {
943 BMCWEB_LOG_ERROR << ec;
944 return;
945 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700946
Ed Tanous002d39b2022-05-31 08:59:27 -0700947 for (const auto& pathStr : subtreePath)
948 {
949 if (pathStr.find("PostCode") != std::string::npos)
950 {
951 nlohmann::json& logServiceArrayLocal =
952 asyncResp->res.jsonValue["Members"];
953 logServiceArrayLocal.push_back(
954 {{"@odata.id",
955 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
956 asyncResp->res.jsonValue["Members@odata.count"] =
957 logServiceArrayLocal.size();
958 return;
959 }
960 }
961 },
962 "xyz.openbmc_project.ObjectMapper",
963 "/xyz/openbmc_project/object_mapper",
964 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
965 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700966 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700967}
968
969inline void requestRoutesEventLogService(App& app)
970{
971 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700972 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700973 .methods(boost::beast::http::verb::get)(
974 [&app](const crow::Request& req,
975 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
976 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
977 {
978 return;
979 }
980 asyncResp->res.jsonValue["@odata.id"] =
981 "/redfish/v1/Systems/system/LogServices/EventLog";
982 asyncResp->res.jsonValue["@odata.type"] =
983 "#LogService.v1_1_0.LogService";
984 asyncResp->res.jsonValue["Name"] = "Event Log Service";
985 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
986 asyncResp->res.jsonValue["Id"] = "EventLog";
987 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530988
Ed Tanous002d39b2022-05-31 08:59:27 -0700989 std::pair<std::string, std::string> redfishDateTimeOffset =
990 crow::utility::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530991
Ed Tanous002d39b2022-05-31 08:59:27 -0700992 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
993 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
994 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +0530995
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
997 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
998 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700999
Ed Tanous002d39b2022-05-31 08:59:27 -07001000 {"target",
1001 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001002 });
1003}
1004
1005inline void requestRoutesJournalEventLogClear(App& app)
1006{
Jason M. Bills4978b632022-02-22 14:17:43 -08001007 BMCWEB_ROUTE(
1008 app,
1009 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001010 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001011 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001012 [&app](const crow::Request& req,
1013 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001014 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1015 {
1016 return;
1017 }
1018 // Clear the EventLog by deleting the log files
1019 std::vector<std::filesystem::path> redfishLogFiles;
1020 if (getRedfishLogFiles(redfishLogFiles))
1021 {
1022 for (const std::filesystem::path& file : redfishLogFiles)
1023 {
1024 std::error_code ec;
1025 std::filesystem::remove(file, ec);
1026 }
1027 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001028
Ed Tanous002d39b2022-05-31 08:59:27 -07001029 // Reload rsyslog so it knows to start new log files
1030 crow::connections::systemBus->async_method_call(
1031 [asyncResp](const boost::system::error_code ec) {
1032 if (ec)
1033 {
1034 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1035 messages::internalError(asyncResp->res);
1036 return;
1037 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001038
Ed Tanous002d39b2022-05-31 08:59:27 -07001039 messages::success(asyncResp->res);
1040 },
1041 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1042 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1043 "replace");
1044 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001045}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001046
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001047static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001048 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001049 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001050{
Jason M. Bills95820182019-04-22 16:25:34 -07001051 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001052 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001053 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001054 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001055 {
1056 return 1;
1057 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001058 std::string timestamp = logEntry.substr(0, space);
1059 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001060 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001061 if (entryStart == std::string::npos)
1062 {
1063 return 1;
1064 }
1065 std::string_view entry(logEntry);
1066 entry.remove_prefix(entryStart);
1067 // Use split to separate the entry into its fields
1068 std::vector<std::string> logEntryFields;
1069 boost::split(logEntryFields, entry, boost::is_any_of(","),
1070 boost::token_compress_on);
1071 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001072 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001073 {
1074 return 1;
1075 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001076 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001077
Jason M. Bills4851d452019-03-28 11:27:48 -07001078 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001079 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001080
Sui Chen54417b02022-03-24 14:59:52 -07001081 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001082 {
Sui Chen54417b02022-03-24 14:59:52 -07001083 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
1084 return 0;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001085 }
1086
Sui Chen54417b02022-03-24 14:59:52 -07001087 std::string msg = message->message;
1088
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001089 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001090 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001091 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001092 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001093 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001094 // If the first string is empty, assume there are no MessageArgs
1095 std::size_t messageArgsSize = 0;
1096 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001097 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001098 messageArgsSize = logEntryFields.size() - 1;
1099 }
1100
Ed Tanous23a21a12020-07-25 04:45:05 +00001101 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001102
1103 // Fill the MessageArgs into the Message
1104 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001105 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001106 {
1107 std::string argStr = "%" + std::to_string(++i);
1108 size_t argPos = msg.find(argStr);
1109 if (argPos != std::string::npos)
1110 {
1111 msg.replace(argPos, argStr.length(), messageArg);
1112 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001113 }
1114 }
1115
Jason M. Bills95820182019-04-22 16:25:34 -07001116 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1117 // format which matches the Redfish format except for the fractional seconds
1118 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001119 std::size_t dot = timestamp.find_first_of('.');
1120 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001121 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001122 {
Jason M. Bills95820182019-04-22 16:25:34 -07001123 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001124 }
1125
1126 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001127 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001128 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001129 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001130 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001131 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001132 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001133 {"Id", logEntryID},
1134 {"Message", std::move(msg)},
1135 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001136 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001137 {"EntryType", "Event"},
Sui Chen54417b02022-03-24 14:59:52 -07001138 {"Severity", message->messageSeverity},
Jason M. Bills95820182019-04-22 16:25:34 -07001139 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001140 return 0;
1141}
1142
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001143inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001144{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001145 BMCWEB_ROUTE(app,
1146 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001147 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001148 .methods(boost::beast::http::verb::get)(
1149 [&app](const crow::Request& req,
1150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1151 query_param::QueryCapabilities capabilities = {
1152 .canDelegateTop = true,
1153 .canDelegateSkip = true,
1154 };
1155 query_param::Query delegatedQuery;
1156 if (!redfish::setUpRedfishRouteWithDelegation(
1157 app, req, asyncResp->res, delegatedQuery, capabilities))
1158 {
1159 return;
1160 }
1161 // Collections don't include the static data added by SubRoute
1162 // because it has a duplicate entry for members
1163 asyncResp->res.jsonValue["@odata.type"] =
1164 "#LogEntryCollection.LogEntryCollection";
1165 asyncResp->res.jsonValue["@odata.id"] =
1166 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1167 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1168 asyncResp->res.jsonValue["Description"] =
1169 "Collection of System Event Log Entries";
1170
1171 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1172 logEntryArray = nlohmann::json::array();
1173 // Go through the log files and create a unique ID for each
1174 // entry
1175 std::vector<std::filesystem::path> redfishLogFiles;
1176 getRedfishLogFiles(redfishLogFiles);
1177 uint64_t entryCount = 0;
1178 std::string logEntry;
1179
1180 // Oldest logs are in the last file, so start there and loop
1181 // backwards
1182 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1183 it++)
1184 {
1185 std::ifstream logStream(*it);
1186 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001187 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001188 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001189 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001190
Ed Tanous002d39b2022-05-31 08:59:27 -07001191 // Reset the unique ID on the first entry
1192 bool firstEntry = true;
1193 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001194 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001195 entryCount++;
1196 // Handle paging using skip (number of entries to skip
1197 // from the start) and top (number of entries to
1198 // display)
1199 if (entryCount <= delegatedQuery.skip ||
1200 entryCount > delegatedQuery.skip + delegatedQuery.top)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001201 {
Jason M. Bills4978b632022-02-22 14:17:43 -08001202 continue;
1203 }
1204
Ed Tanous002d39b2022-05-31 08:59:27 -07001205 std::string idStr;
1206 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001207 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001208 continue;
1209 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001210
Ed Tanous002d39b2022-05-31 08:59:27 -07001211 if (firstEntry)
1212 {
1213 firstEntry = false;
1214 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001215
Ed Tanous002d39b2022-05-31 08:59:27 -07001216 logEntryArray.push_back({});
1217 nlohmann::json& bmcLogEntry = logEntryArray.back();
1218 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1219 {
1220 messages::internalError(asyncResp->res);
1221 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001222 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001223 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001224 }
1225 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1226 if (delegatedQuery.skip + delegatedQuery.top < entryCount)
1227 {
1228 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1229 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
1230 std::to_string(delegatedQuery.skip + delegatedQuery.top);
1231 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001232 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001233}
Chicago Duan336e96c2019-07-15 14:22:08 +08001234
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001235inline void requestRoutesJournalEventLogEntry(App& app)
1236{
1237 BMCWEB_ROUTE(
1238 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001239 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001240 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001241 [&app](const crow::Request& req,
1242 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1243 const std::string& param) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001244 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1245 {
1246 return;
1247 }
1248 const std::string& targetID = param;
1249
1250 // Go through the log files and check the unique ID for each
1251 // entry to find the target entry
1252 std::vector<std::filesystem::path> redfishLogFiles;
1253 getRedfishLogFiles(redfishLogFiles);
1254 std::string logEntry;
1255
1256 // Oldest logs are in the last file, so start there and loop
1257 // backwards
1258 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1259 it++)
1260 {
1261 std::ifstream logStream(*it);
1262 if (!logStream.is_open())
1263 {
1264 continue;
1265 }
1266
1267 // Reset the unique ID on the first entry
1268 bool firstEntry = true;
1269 while (std::getline(logStream, logEntry))
1270 {
1271 std::string idStr;
1272 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001273 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001274 continue;
1275 }
1276
1277 if (firstEntry)
1278 {
1279 firstEntry = false;
1280 }
1281
1282 if (idStr == targetID)
1283 {
1284 if (fillEventLogEntryJson(idStr, logEntry,
1285 asyncResp->res.jsonValue) != 0)
1286 {
1287 messages::internalError(asyncResp->res);
1288 return;
1289 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07001290 return;
1291 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001292 }
1293 }
1294 // Requested ID was not found
1295 messages::resourceMissingAtURI(asyncResp->res,
1296 crow::utility::urlFromPieces(targetID));
1297 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001298}
1299
1300inline void requestRoutesDBusEventLogEntryCollection(App& app)
1301{
1302 BMCWEB_ROUTE(app,
1303 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001304 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001305 .methods(boost::beast::http::verb::get)(
1306 [&app](const crow::Request& req,
1307 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1308 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1309 {
1310 return;
1311 }
1312 // Collections don't include the static data added by SubRoute
1313 // because it has a duplicate entry for members
1314 asyncResp->res.jsonValue["@odata.type"] =
1315 "#LogEntryCollection.LogEntryCollection";
1316 asyncResp->res.jsonValue["@odata.id"] =
1317 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1318 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1319 asyncResp->res.jsonValue["Description"] =
1320 "Collection of System Event Log Entries";
1321
1322 // DBus implementation of EventLog/Entries
1323 // Make call to Logging Service to find all log entry objects
1324 crow::connections::systemBus->async_method_call(
1325 [asyncResp](const boost::system::error_code ec,
1326 const dbus::utility::ManagedObjectType& resp) {
1327 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001328 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001329 // TODO Handle for specific error code
1330 BMCWEB_LOG_ERROR
1331 << "getLogEntriesIfaceData resp_handler got error " << ec;
1332 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001333 return;
1334 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001335 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1336 entriesArray = nlohmann::json::array();
1337 for (const auto& objectPath : resp)
1338 {
1339 const uint32_t* id = nullptr;
1340 const uint64_t* timestamp = nullptr;
1341 const uint64_t* updateTimestamp = nullptr;
1342 const std::string* severity = nullptr;
1343 const std::string* message = nullptr;
1344 const std::string* filePath = nullptr;
1345 bool resolved = false;
1346 for (const auto& interfaceMap : objectPath.second)
1347 {
1348 if (interfaceMap.first ==
1349 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001350 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001351 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001352 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001353 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001354 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001355 id = std::get_if<uint32_t>(&propertyMap.second);
1356 }
1357 else if (propertyMap.first == "Timestamp")
1358 {
1359 timestamp =
1360 std::get_if<uint64_t>(&propertyMap.second);
1361 }
1362 else if (propertyMap.first == "UpdateTimestamp")
1363 {
1364 updateTimestamp =
1365 std::get_if<uint64_t>(&propertyMap.second);
1366 }
1367 else if (propertyMap.first == "Severity")
1368 {
1369 severity = std::get_if<std::string>(
1370 &propertyMap.second);
1371 }
1372 else if (propertyMap.first == "Message")
1373 {
1374 message = std::get_if<std::string>(
1375 &propertyMap.second);
1376 }
1377 else if (propertyMap.first == "Resolved")
1378 {
1379 const bool* resolveptr =
1380 std::get_if<bool>(&propertyMap.second);
1381 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001382 {
1383 messages::internalError(asyncResp->res);
1384 return;
1385 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001386 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001387 }
1388 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001389 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001390 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001391 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001392 messages::internalError(asyncResp->res);
1393 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001394 }
1395 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001396 else if (interfaceMap.first ==
1397 "xyz.openbmc_project.Common.FilePath")
1398 {
1399 for (const auto& propertyMap : interfaceMap.second)
1400 {
1401 if (propertyMap.first == "Path")
1402 {
1403 filePath = std::get_if<std::string>(
1404 &propertyMap.second);
1405 }
1406 }
1407 }
1408 }
1409 // Object path without the
1410 // xyz.openbmc_project.Logging.Entry interface, ignore
1411 // and continue.
1412 if (id == nullptr || message == nullptr ||
1413 severity == nullptr || timestamp == nullptr ||
1414 updateTimestamp == nullptr)
1415 {
1416 continue;
1417 }
1418 entriesArray.push_back({});
1419 nlohmann::json& thisEntry = entriesArray.back();
1420 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1421 thisEntry["@odata.id"] =
1422 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1423 std::to_string(*id);
1424 thisEntry["Name"] = "System Event Log Entry";
1425 thisEntry["Id"] = std::to_string(*id);
1426 thisEntry["Message"] = *message;
1427 thisEntry["Resolved"] = resolved;
1428 thisEntry["EntryType"] = "Event";
1429 thisEntry["Severity"] =
1430 translateSeverityDbusToRedfish(*severity);
1431 thisEntry["Created"] =
1432 crow::utility::getDateTimeUintMs(*timestamp);
1433 thisEntry["Modified"] =
1434 crow::utility::getDateTimeUintMs(*updateTimestamp);
1435 if (filePath != nullptr)
1436 {
1437 thisEntry["AdditionalDataURI"] =
1438 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1439 std::to_string(*id) + "/attachment";
1440 }
1441 }
1442 std::sort(
1443 entriesArray.begin(), entriesArray.end(),
1444 [](const nlohmann::json& left, const nlohmann::json& right) {
1445 return (left["Id"] <= right["Id"]);
1446 });
1447 asyncResp->res.jsonValue["Members@odata.count"] =
1448 entriesArray.size();
1449 },
1450 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1451 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001452 });
1453}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001454
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001455inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001456{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001457 BMCWEB_ROUTE(
1458 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001459 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001460 .methods(boost::beast::http::verb::get)(
1461 [&app](const crow::Request& req,
1462 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1463 const std::string& param) {
1464 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1465 {
1466 return;
1467 }
1468 std::string entryID = param;
1469 dbus::utility::escapePathForDbus(entryID);
1470
1471 // DBus implementation of EventLog/Entries
1472 // Make call to Logging Service to find all log entry objects
1473 crow::connections::systemBus->async_method_call(
1474 [asyncResp, entryID](const boost::system::error_code ec,
1475 const dbus::utility::DBusPropertiesMap& resp) {
1476 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001477 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001478 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1479 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001480 return;
1481 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001482 if (ec)
1483 {
1484 BMCWEB_LOG_ERROR
1485 << "EventLogEntry (DBus) resp_handler got error " << ec;
1486 messages::internalError(asyncResp->res);
1487 return;
1488 }
1489 const uint32_t* id = nullptr;
1490 const uint64_t* timestamp = nullptr;
1491 const uint64_t* updateTimestamp = nullptr;
1492 const std::string* severity = nullptr;
1493 const std::string* message = nullptr;
1494 const std::string* filePath = nullptr;
1495 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001496
Ed Tanous002d39b2022-05-31 08:59:27 -07001497 for (const auto& propertyMap : resp)
1498 {
1499 if (propertyMap.first == "Id")
1500 {
1501 id = std::get_if<uint32_t>(&propertyMap.second);
1502 }
1503 else if (propertyMap.first == "Timestamp")
1504 {
1505 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1506 }
1507 else if (propertyMap.first == "UpdateTimestamp")
1508 {
1509 updateTimestamp =
1510 std::get_if<uint64_t>(&propertyMap.second);
1511 }
1512 else if (propertyMap.first == "Severity")
1513 {
1514 severity = std::get_if<std::string>(&propertyMap.second);
1515 }
1516 else if (propertyMap.first == "Message")
1517 {
1518 message = std::get_if<std::string>(&propertyMap.second);
1519 }
1520 else if (propertyMap.first == "Resolved")
1521 {
1522 const bool* resolveptr =
1523 std::get_if<bool>(&propertyMap.second);
1524 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001525 {
1526 messages::internalError(asyncResp->res);
1527 return;
1528 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001529 resolved = *resolveptr;
1530 }
1531 else if (propertyMap.first == "Path")
1532 {
1533 filePath = std::get_if<std::string>(&propertyMap.second);
1534 }
1535 }
1536 if (id == nullptr || message == nullptr || severity == nullptr ||
1537 timestamp == nullptr || updateTimestamp == nullptr)
1538 {
1539 messages::internalError(asyncResp->res);
1540 return;
1541 }
1542 asyncResp->res.jsonValue["@odata.type"] =
1543 "#LogEntry.v1_8_0.LogEntry";
1544 asyncResp->res.jsonValue["@odata.id"] =
1545 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1546 std::to_string(*id);
1547 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1548 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1549 asyncResp->res.jsonValue["Message"] = *message;
1550 asyncResp->res.jsonValue["Resolved"] = resolved;
1551 asyncResp->res.jsonValue["EntryType"] = "Event";
1552 asyncResp->res.jsonValue["Severity"] =
1553 translateSeverityDbusToRedfish(*severity);
1554 asyncResp->res.jsonValue["Created"] =
1555 crow::utility::getDateTimeUintMs(*timestamp);
1556 asyncResp->res.jsonValue["Modified"] =
1557 crow::utility::getDateTimeUintMs(*updateTimestamp);
1558 if (filePath != nullptr)
1559 {
1560 asyncResp->res.jsonValue["AdditionalDataURI"] =
1561 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1562 std::to_string(*id) + "/attachment";
1563 }
1564 },
1565 "xyz.openbmc_project.Logging",
1566 "/xyz/openbmc_project/logging/entry/" + entryID,
1567 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001568 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001569
1570 BMCWEB_ROUTE(
1571 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001572 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001573 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001574 [&app](const crow::Request& req,
1575 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1576 const std::string& entryId) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001577 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1578 {
1579 return;
1580 }
1581 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001582
Ed Tanous002d39b2022-05-31 08:59:27 -07001583 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1584 resolved))
1585 {
1586 return;
1587 }
1588 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001589
Ed Tanous002d39b2022-05-31 08:59:27 -07001590 crow::connections::systemBus->async_method_call(
1591 [asyncResp, entryId](const boost::system::error_code ec) {
1592 if (ec)
1593 {
1594 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1595 messages::internalError(asyncResp->res);
1596 return;
1597 }
1598 },
1599 "xyz.openbmc_project.Logging",
1600 "/xyz/openbmc_project/logging/entry/" + entryId,
1601 "org.freedesktop.DBus.Properties", "Set",
1602 "xyz.openbmc_project.Logging.Entry", "Resolved",
1603 dbus::utility::DbusVariantType(*resolved));
1604 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001605
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001606 BMCWEB_ROUTE(
1607 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001608 .privileges(redfish::privileges::deleteLogEntry)
1609
Ed Tanous002d39b2022-05-31 08:59:27 -07001610 .methods(boost::beast::http::verb::delete_)(
1611 [&app](const crow::Request& req,
1612 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1613 const std::string& param) {
1614 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1615 {
1616 return;
1617 }
1618 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1619
1620 std::string entryID = param;
1621
1622 dbus::utility::escapePathForDbus(entryID);
1623
1624 // Process response from Logging service.
1625 auto respHandler =
1626 [asyncResp, entryID](const boost::system::error_code ec) {
1627 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1628 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001629 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001630 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001631 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001632 messages::resourceNotFound(asyncResp->res, "LogEntry",
1633 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001634 return;
1635 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001636 // TODO Handle for specific error code
1637 BMCWEB_LOG_ERROR
1638 << "EventLogEntry (DBus) doDelete respHandler got error "
1639 << ec;
1640 asyncResp->res.result(
1641 boost::beast::http::status::internal_server_error);
1642 return;
1643 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001644
Ed Tanous002d39b2022-05-31 08:59:27 -07001645 asyncResp->res.result(boost::beast::http::status::ok);
1646 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001647
Ed Tanous002d39b2022-05-31 08:59:27 -07001648 // Make call to Logging service to request Delete Log
1649 crow::connections::systemBus->async_method_call(
1650 respHandler, "xyz.openbmc_project.Logging",
1651 "/xyz/openbmc_project/logging/entry/" + entryID,
1652 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001653 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001654}
1655
1656inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001657{
George Liu0fda0f12021-11-16 10:06:17 +08001658 BMCWEB_ROUTE(
1659 app,
1660 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001661 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001662 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001663 [&app](const crow::Request& req,
1664 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1665 const std::string& param) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001666 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1667 {
1668 return;
1669 }
1670 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1671 {
1672 asyncResp->res.result(boost::beast::http::status::bad_request);
1673 return;
1674 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001675
Ed Tanous002d39b2022-05-31 08:59:27 -07001676 std::string entryID = param;
1677 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001678
Ed Tanous002d39b2022-05-31 08:59:27 -07001679 crow::connections::systemBus->async_method_call(
1680 [asyncResp, entryID](const boost::system::error_code ec,
1681 const sdbusplus::message::unix_fd& unixfd) {
1682 if (ec.value() == EBADR)
1683 {
1684 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1685 entryID);
1686 return;
1687 }
1688 if (ec)
1689 {
1690 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1691 messages::internalError(asyncResp->res);
1692 return;
1693 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001694
Ed Tanous002d39b2022-05-31 08:59:27 -07001695 int fd = -1;
1696 fd = dup(unixfd);
1697 if (fd == -1)
1698 {
1699 messages::internalError(asyncResp->res);
1700 return;
1701 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001702
Ed Tanous002d39b2022-05-31 08:59:27 -07001703 long long int size = lseek(fd, 0, SEEK_END);
1704 if (size == -1)
1705 {
1706 messages::internalError(asyncResp->res);
1707 return;
1708 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001709
Ed Tanous002d39b2022-05-31 08:59:27 -07001710 // Arbitrary max size of 64kb
1711 constexpr int maxFileSize = 65536;
1712 if (size > maxFileSize)
1713 {
1714 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1715 << maxFileSize;
1716 messages::internalError(asyncResp->res);
1717 return;
1718 }
1719 std::vector<char> data(static_cast<size_t>(size));
1720 long long int rc = lseek(fd, 0, SEEK_SET);
1721 if (rc == -1)
1722 {
1723 messages::internalError(asyncResp->res);
1724 return;
1725 }
1726 rc = read(fd, data.data(), data.size());
1727 if ((rc == -1) || (rc != size))
1728 {
1729 messages::internalError(asyncResp->res);
1730 return;
1731 }
1732 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001733
Ed Tanous002d39b2022-05-31 08:59:27 -07001734 std::string_view strData(data.data(), data.size());
1735 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001736
Ed Tanous002d39b2022-05-31 08:59:27 -07001737 asyncResp->res.addHeader("Content-Type",
1738 "application/octet-stream");
1739 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
1740 asyncResp->res.body() = std::move(output);
1741 },
1742 "xyz.openbmc_project.Logging",
1743 "/xyz/openbmc_project/logging/entry/" + entryID,
1744 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1745 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001746}
1747
Spencer Kub7028eb2021-10-26 15:27:35 +08001748constexpr const char* hostLoggerFolderPath = "/var/log/console";
1749
1750inline bool
1751 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1752 std::vector<std::filesystem::path>& hostLoggerFiles)
1753{
1754 std::error_code ec;
1755 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1756 if (ec)
1757 {
1758 BMCWEB_LOG_ERROR << ec.message();
1759 return false;
1760 }
1761 for (const std::filesystem::directory_entry& it : logPath)
1762 {
1763 std::string filename = it.path().filename();
1764 // Prefix of each log files is "log". Find the file and save the
1765 // path
1766 if (boost::starts_with(filename, "log"))
1767 {
1768 hostLoggerFiles.emplace_back(it.path());
1769 }
1770 }
1771 // As the log files rotate, they are appended with a ".#" that is higher for
1772 // the older logs. Since we start from oldest logs, sort the name in
1773 // descending order.
1774 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1775 AlphanumLess<std::string>());
1776
1777 return true;
1778}
1779
1780inline bool
1781 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
Ed Tanousc937d2b2022-04-05 09:58:00 -07001782 uint64_t skip, uint64_t top,
Spencer Kub7028eb2021-10-26 15:27:35 +08001783 std::vector<std::string>& logEntries, size_t& logCount)
1784{
1785 GzFileReader logFile;
1786
1787 // Go though all log files and expose host logs.
1788 for (const std::filesystem::path& it : hostLoggerFiles)
1789 {
1790 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1791 {
1792 BMCWEB_LOG_ERROR << "fail to expose host logs";
1793 return false;
1794 }
1795 }
1796 // Get lastMessage from constructor by getter
1797 std::string lastMessage = logFile.getLastMessage();
1798 if (!lastMessage.empty())
1799 {
1800 logCount++;
1801 if (logCount > skip && logCount <= (skip + top))
1802 {
1803 logEntries.push_back(lastMessage);
1804 }
1805 }
1806 return true;
1807}
1808
1809inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1810 const std::string& msg,
1811 nlohmann::json& logEntryJson)
1812{
1813 // Fill in the log entry with the gathered data.
1814 logEntryJson = {
1815 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1816 {"@odata.id",
1817 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1818 logEntryID},
1819 {"Name", "Host Logger Entry"},
1820 {"Id", logEntryID},
1821 {"Message", msg},
1822 {"EntryType", "Oem"},
1823 {"Severity", "OK"},
1824 {"OemRecordFormat", "Host Logger Entry"}};
1825}
1826
1827inline void requestRoutesSystemHostLogger(App& app)
1828{
1829 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1830 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001831 .methods(boost::beast::http::verb::get)(
1832 [&app](const crow::Request& req,
1833 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001834 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1835 {
1836 return;
1837 }
1838 asyncResp->res.jsonValue["@odata.id"] =
1839 "/redfish/v1/Systems/system/LogServices/HostLogger";
1840 asyncResp->res.jsonValue["@odata.type"] =
1841 "#LogService.v1_1_0.LogService";
1842 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1843 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1844 asyncResp->res.jsonValue["Id"] = "HostLogger";
1845 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1846 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1847 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001848}
1849
1850inline void requestRoutesSystemHostLoggerCollection(App& app)
1851{
1852 BMCWEB_ROUTE(app,
1853 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1854 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001855 .methods(boost::beast::http::verb::get)(
1856 [&app](const crow::Request& req,
1857 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1858 query_param::QueryCapabilities capabilities = {
1859 .canDelegateTop = true,
1860 .canDelegateSkip = true,
1861 };
1862 query_param::Query delegatedQuery;
1863 if (!redfish::setUpRedfishRouteWithDelegation(
1864 app, req, asyncResp->res, delegatedQuery, capabilities))
1865 {
1866 return;
1867 }
1868 asyncResp->res.jsonValue["@odata.id"] =
1869 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1870 asyncResp->res.jsonValue["@odata.type"] =
1871 "#LogEntryCollection.LogEntryCollection";
1872 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1873 asyncResp->res.jsonValue["Description"] =
1874 "Collection of HostLogger Entries";
1875 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1876 logEntryArray = nlohmann::json::array();
1877 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001878
Ed Tanous002d39b2022-05-31 08:59:27 -07001879 std::vector<std::filesystem::path> hostLoggerFiles;
1880 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1881 {
1882 BMCWEB_LOG_ERROR << "fail to get host log file path";
1883 return;
1884 }
1885
1886 size_t logCount = 0;
1887 // This vector only store the entries we want to expose that
1888 // control by skip and top.
1889 std::vector<std::string> logEntries;
1890 if (!getHostLoggerEntries(hostLoggerFiles, delegatedQuery.skip,
1891 delegatedQuery.top, logEntries, logCount))
1892 {
1893 messages::internalError(asyncResp->res);
1894 return;
1895 }
1896 // If vector is empty, that means skip value larger than total
1897 // log count
1898 if (logEntries.empty())
1899 {
1900 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1901 return;
1902 }
1903 if (!logEntries.empty())
1904 {
1905 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001906 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001907 logEntryArray.push_back({});
1908 nlohmann::json& hostLogEntry = logEntryArray.back();
1909 fillHostLoggerEntryJson(std::to_string(delegatedQuery.skip + i),
1910 logEntries[i], hostLogEntry);
George Liu0fda0f12021-11-16 10:06:17 +08001911 }
1912
Ed Tanous002d39b2022-05-31 08:59:27 -07001913 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1914 if (delegatedQuery.skip + delegatedQuery.top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001915 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001916 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1917 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
1918 std::to_string(delegatedQuery.skip + delegatedQuery.top);
George Liu0fda0f12021-11-16 10:06:17 +08001919 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001920 }
George Liu0fda0f12021-11-16 10:06:17 +08001921 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001922}
1923
1924inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1925{
1926 BMCWEB_ROUTE(
1927 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1928 .privileges(redfish::privileges::getLogEntry)
1929 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001930 [&app](const crow::Request& req,
1931 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1932 const std::string& param) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001933 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1934 {
1935 return;
1936 }
1937 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001938
Ed Tanous002d39b2022-05-31 08:59:27 -07001939 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001940
Ed Tanous002d39b2022-05-31 08:59:27 -07001941 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1942 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001943
Ed Tanous002d39b2022-05-31 08:59:27 -07001944 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1945 if (ec == std::errc::invalid_argument)
1946 {
1947 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1948 return;
1949 }
1950 if (ec == std::errc::result_out_of_range)
1951 {
1952 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1953 return;
1954 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001955
Ed Tanous002d39b2022-05-31 08:59:27 -07001956 std::vector<std::filesystem::path> hostLoggerFiles;
1957 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1958 {
1959 BMCWEB_LOG_ERROR << "fail to get host log file path";
1960 return;
1961 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001962
Ed Tanous002d39b2022-05-31 08:59:27 -07001963 size_t logCount = 0;
1964 uint64_t top = 1;
1965 std::vector<std::string> logEntries;
1966 // We can get specific entry by skip and top. For example, if we
1967 // want to get nth entry, we can set skip = n-1 and top = 1 to
1968 // get that entry
1969 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1970 logCount))
1971 {
1972 messages::internalError(asyncResp->res);
1973 return;
1974 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001975
Ed Tanous002d39b2022-05-31 08:59:27 -07001976 if (!logEntries.empty())
1977 {
1978 fillHostLoggerEntryJson(targetID, logEntries[0],
1979 asyncResp->res.jsonValue);
1980 return;
1981 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001982
Ed Tanous002d39b2022-05-31 08:59:27 -07001983 // Requested ID was not found
1984 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1985 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001986}
1987
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001988inline void requestRoutesBMCLogServiceCollection(App& app)
1989{
1990 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05001991 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001992 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001993 [&app](const crow::Request& req,
1994 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07001995 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
1996 {
1997 return;
1998 }
1999 // Collections don't include the static data added by SubRoute
2000 // because it has a duplicate entry for members
2001 asyncResp->res.jsonValue["@odata.type"] =
2002 "#LogServiceCollection.LogServiceCollection";
2003 asyncResp->res.jsonValue["@odata.id"] =
2004 "/redfish/v1/Managers/bmc/LogServices";
2005 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2006 asyncResp->res.jsonValue["Description"] =
2007 "Collection of LogServices for this Manager";
2008 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2009 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002010#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -07002011 logServiceArray.push_back(
2012 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002013#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002014#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
Ed Tanous002d39b2022-05-31 08:59:27 -07002015 logServiceArray.push_back(
2016 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002017#endif
Ed Tanous002d39b2022-05-31 08:59:27 -07002018 asyncResp->res.jsonValue["Members@odata.count"] =
2019 logServiceArray.size();
2020 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002021}
Ed Tanous1da66f72018-07-27 16:13:37 -07002022
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002023inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002024{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002025 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002026 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002027 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002028 [&app](const crow::Request& req,
2029 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002030 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2031 {
2032 return;
2033 }
2034 asyncResp->res.jsonValue["@odata.type"] =
2035 "#LogService.v1_1_0.LogService";
2036 asyncResp->res.jsonValue["@odata.id"] =
2037 "/redfish/v1/Managers/bmc/LogServices/Journal";
2038 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2039 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2040 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2041 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302042
Ed Tanous002d39b2022-05-31 08:59:27 -07002043 std::pair<std::string, std::string> redfishDateTimeOffset =
2044 crow::utility::getDateTimeOffsetNow();
2045 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2046 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2047 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302048
Ed Tanous002d39b2022-05-31 08:59:27 -07002049 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2050 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2051 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002052}
Jason M. Billse1f26342018-07-18 12:12:00 -07002053
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002054static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2055 sd_journal* journal,
2056 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002057{
2058 // Get the Log Entry contents
2059 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002060
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002061 std::string message;
2062 std::string_view syslogID;
2063 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2064 if (ret < 0)
2065 {
2066 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2067 << strerror(-ret);
2068 }
2069 if (!syslogID.empty())
2070 {
2071 message += std::string(syslogID) + ": ";
2072 }
2073
Ed Tanous39e77502019-03-04 17:35:53 -08002074 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002075 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002076 if (ret < 0)
2077 {
2078 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2079 return 1;
2080 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002081 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002082
2083 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002084 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002085 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002086 if (ret < 0)
2087 {
2088 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002089 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002090
2091 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002092 std::string entryTimeStr;
2093 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002094 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002095 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002096 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002097
2098 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002099 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002100 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002101 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2102 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002103 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002104 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002105 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002106 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002107 {"Severity", severity <= 2 ? "Critical"
2108 : severity <= 4 ? "Warning"
2109 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002110 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002111 {"Created", std::move(entryTimeStr)}};
2112 return 0;
2113}
2114
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002115inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002116{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002117 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002118 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002119 .methods(boost::beast::http::verb::get)(
2120 [&app](const crow::Request& req,
2121 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2122 query_param::QueryCapabilities capabilities = {
2123 .canDelegateTop = true,
2124 .canDelegateSkip = true,
2125 };
2126 query_param::Query delegatedQuery;
2127 if (!redfish::setUpRedfishRouteWithDelegation(
2128 app, req, asyncResp->res, delegatedQuery, capabilities))
2129 {
2130 return;
2131 }
2132 // Collections don't include the static data added by SubRoute
2133 // because it has a duplicate entry for members
2134 asyncResp->res.jsonValue["@odata.type"] =
2135 "#LogEntryCollection.LogEntryCollection";
2136 asyncResp->res.jsonValue["@odata.id"] =
2137 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2138 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2139 asyncResp->res.jsonValue["Description"] =
2140 "Collection of BMC Journal Entries";
2141 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2142 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002143
Ed Tanous002d39b2022-05-31 08:59:27 -07002144 // Go through the journal and use the timestamp to create a
2145 // unique ID for each entry
2146 sd_journal* journalTmp = nullptr;
2147 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2148 if (ret < 0)
2149 {
2150 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2151 messages::internalError(asyncResp->res);
2152 return;
2153 }
2154 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2155 journalTmp, sd_journal_close);
2156 journalTmp = nullptr;
2157 uint64_t entryCount = 0;
2158 // Reset the unique ID on the first entry
2159 bool firstEntry = true;
2160 SD_JOURNAL_FOREACH(journal.get())
2161 {
2162 entryCount++;
2163 // Handle paging using skip (number of entries to skip from
2164 // the start) and top (number of entries to display)
2165 if (entryCount <= delegatedQuery.skip ||
2166 entryCount > delegatedQuery.skip + delegatedQuery.top)
George Liu0fda0f12021-11-16 10:06:17 +08002167 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002168 continue;
2169 }
2170
2171 std::string idStr;
2172 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2173 {
2174 continue;
2175 }
2176
2177 if (firstEntry)
2178 {
2179 firstEntry = false;
2180 }
2181
2182 logEntryArray.push_back({});
2183 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2184 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2185 bmcJournalLogEntry) != 0)
2186 {
George Liu0fda0f12021-11-16 10:06:17 +08002187 messages::internalError(asyncResp->res);
2188 return;
2189 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002190 }
2191 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2192 if (delegatedQuery.skip + delegatedQuery.top < entryCount)
2193 {
2194 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2195 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2196 std::to_string(delegatedQuery.skip + delegatedQuery.top);
2197 }
George Liu0fda0f12021-11-16 10:06:17 +08002198 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002199}
Jason M. Billse1f26342018-07-18 12:12:00 -07002200
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002201inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002202{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002203 BMCWEB_ROUTE(app,
2204 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002205 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002206 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002207 [&app](const crow::Request& req,
2208 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2209 const std::string& entryID) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002210 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2211 {
2212 return;
2213 }
2214 // Convert the unique ID back to a timestamp to find the entry
2215 uint64_t ts = 0;
2216 uint64_t index = 0;
2217 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2218 {
2219 return;
2220 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002221
Ed Tanous002d39b2022-05-31 08:59:27 -07002222 sd_journal* journalTmp = nullptr;
2223 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2224 if (ret < 0)
2225 {
2226 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2227 messages::internalError(asyncResp->res);
2228 return;
2229 }
2230 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2231 journalTmp, sd_journal_close);
2232 journalTmp = nullptr;
2233 // Go to the timestamp in the log and move to the entry at the
2234 // index tracking the unique ID
2235 std::string idStr;
2236 bool firstEntry = true;
2237 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2238 if (ret < 0)
2239 {
2240 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2241 << strerror(-ret);
2242 messages::internalError(asyncResp->res);
2243 return;
2244 }
2245 for (uint64_t i = 0; i <= index; i++)
2246 {
2247 sd_journal_next(journal.get());
2248 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2249 {
2250 messages::internalError(asyncResp->res);
2251 return;
2252 }
2253 if (firstEntry)
2254 {
2255 firstEntry = false;
2256 }
2257 }
2258 // Confirm that the entry ID matches what was requested
2259 if (idStr != entryID)
2260 {
2261 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2262 return;
2263 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002264
Ed Tanous002d39b2022-05-31 08:59:27 -07002265 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2266 asyncResp->res.jsonValue) != 0)
2267 {
2268 messages::internalError(asyncResp->res);
2269 return;
2270 }
2271 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002272}
2273
2274inline void requestRoutesBMCDumpService(App& app)
2275{
2276 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002277 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002278 .methods(boost::beast::http::verb::get)(
2279 [&app](const crow::Request& req,
2280 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2281 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2282 {
2283 return;
2284 }
2285 asyncResp->res.jsonValue["@odata.id"] =
2286 "/redfish/v1/Managers/bmc/LogServices/Dump";
2287 asyncResp->res.jsonValue["@odata.type"] =
2288 "#LogService.v1_2_0.LogService";
2289 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2290 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2291 asyncResp->res.jsonValue["Id"] = "Dump";
2292 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302293
Ed Tanous002d39b2022-05-31 08:59:27 -07002294 std::pair<std::string, std::string> redfishDateTimeOffset =
2295 crow::utility::getDateTimeOffsetNow();
2296 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2297 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2298 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302299
Ed Tanous002d39b2022-05-31 08:59:27 -07002300 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2301 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2302 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2303 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog";
2304 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2305 ["target"] =
2306 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
George Liu0fda0f12021-11-16 10:06:17 +08002307 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002308}
2309
2310inline void requestRoutesBMCDumpEntryCollection(App& app)
2311{
2312
2313 /**
2314 * Functions triggers appropriate requests on DBus
2315 */
2316 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002317 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002318 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002319 [&app](const crow::Request& req,
2320 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002321 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2322 {
2323 return;
2324 }
2325 asyncResp->res.jsonValue["@odata.type"] =
2326 "#LogEntryCollection.LogEntryCollection";
2327 asyncResp->res.jsonValue["@odata.id"] =
2328 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2329 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2330 asyncResp->res.jsonValue["Description"] =
2331 "Collection of BMC Dump Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002332
Ed Tanous002d39b2022-05-31 08:59:27 -07002333 getDumpEntryCollection(asyncResp, "BMC");
2334 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002335}
2336
2337inline void requestRoutesBMCDumpEntry(App& app)
2338{
2339 BMCWEB_ROUTE(app,
2340 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002341 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002342 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002343 [&app](const crow::Request& req,
2344 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2345 const std::string& param) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002346 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2347 {
2348 return;
2349 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002350
Ed Tanous002d39b2022-05-31 08:59:27 -07002351 getDumpEntryById(app, req, asyncResp, param, "BMC");
2352 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002353 BMCWEB_ROUTE(app,
2354 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002355 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002356 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002357 [&app](const crow::Request& req,
2358 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2359 const std::string& param) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002360 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2361 {
2362 return;
2363 }
2364 deleteDumpEntry(asyncResp, param, "bmc");
2365 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002366}
2367
2368inline void requestRoutesBMCDumpCreate(App& app)
2369{
George Liu0fda0f12021-11-16 10:06:17 +08002370 BMCWEB_ROUTE(
2371 app,
2372 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002373 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002374 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002375 [&app](const crow::Request& req,
2376 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002377 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2378 {
2379 return;
2380 }
2381 createDump(asyncResp, req, "BMC");
2382 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002383}
2384
2385inline void requestRoutesBMCDumpClear(App& app)
2386{
George Liu0fda0f12021-11-16 10:06:17 +08002387 BMCWEB_ROUTE(
2388 app,
2389 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002390 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002391 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002392 [&app](const crow::Request& req,
2393 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002394 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2395 {
2396 return;
2397 }
2398 clearDump(asyncResp, "BMC");
2399 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002400}
2401
2402inline void requestRoutesSystemDumpService(App& app)
2403{
2404 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002405 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002406 .methods(boost::beast::http::verb::get)(
2407 [&app](const crow::Request& req,
2408 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2409 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2410 {
2411 return;
2412 }
2413 asyncResp->res.jsonValue["@odata.id"] =
2414 "/redfish/v1/Systems/system/LogServices/Dump";
2415 asyncResp->res.jsonValue["@odata.type"] =
2416 "#LogService.v1_2_0.LogService";
2417 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2418 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2419 asyncResp->res.jsonValue["Id"] = "Dump";
2420 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302421
Ed Tanous002d39b2022-05-31 08:59:27 -07002422 std::pair<std::string, std::string> redfishDateTimeOffset =
2423 crow::utility::getDateTimeOffsetNow();
2424 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2425 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2426 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302427
Ed Tanous002d39b2022-05-31 08:59:27 -07002428 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2429 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2430 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2431 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002432
Ed Tanous002d39b2022-05-31 08:59:27 -07002433 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2434 ["target"] =
2435 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002436 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002437}
2438
2439inline void requestRoutesSystemDumpEntryCollection(App& app)
2440{
2441
2442 /**
2443 * Functions triggers appropriate requests on DBus
2444 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002445 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002446 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002447 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002448 [&app](const crow::Request& req,
2449 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002450 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2451 {
2452 return;
2453 }
2454 asyncResp->res.jsonValue["@odata.type"] =
2455 "#LogEntryCollection.LogEntryCollection";
2456 asyncResp->res.jsonValue["@odata.id"] =
2457 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2458 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2459 asyncResp->res.jsonValue["Description"] =
2460 "Collection of System Dump Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002461
Ed Tanous002d39b2022-05-31 08:59:27 -07002462 getDumpEntryCollection(asyncResp, "System");
2463 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002464}
2465
2466inline void requestRoutesSystemDumpEntry(App& app)
2467{
2468 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002469 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002470 .privileges(redfish::privileges::getLogEntry)
2471
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002472 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002473 [&app](const crow::Request& req,
2474 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2475 const std::string& param) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002476 getDumpEntryById(app, req, asyncResp, param, "System");
2477 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002478
2479 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002480 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002481 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002482 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002483 [&app](const crow::Request& req,
2484 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2485 const std::string& param) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002486 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2487 {
2488 return;
2489 }
2490 deleteDumpEntry(asyncResp, param, "system");
2491 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002492}
2493
2494inline void requestRoutesSystemDumpCreate(App& app)
2495{
George Liu0fda0f12021-11-16 10:06:17 +08002496 BMCWEB_ROUTE(
2497 app,
2498 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002499 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002500 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002501 [&app](const crow::Request& req,
2502 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002503 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2504 {
2505 return;
2506 }
2507 createDump(asyncResp, req, "System");
2508 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002509}
2510
2511inline void requestRoutesSystemDumpClear(App& app)
2512{
George Liu0fda0f12021-11-16 10:06:17 +08002513 BMCWEB_ROUTE(
2514 app,
2515 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002516 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002517 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002518 [&app](const crow::Request& req,
2519 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002520
Ed Tanous45ca1b82022-03-25 13:07:27 -07002521 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002522 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2523 {
2524 return;
2525 }
2526 clearDump(asyncResp, "System");
2527 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002528}
2529
2530inline void requestRoutesCrashdumpService(App& app)
2531{
2532 // Note: Deviated from redfish privilege registry for GET & HEAD
2533 // method for security reasons.
2534 /**
2535 * Functions triggers appropriate requests on DBus
2536 */
2537 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002538 // This is incorrect, should be:
2539 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002540 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002541 .methods(boost::beast::http::verb::get)(
2542 [&app](const crow::Request& req,
2543 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2544 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2545 {
2546 return;
2547 }
2548 // Copy over the static data to include the entries added by
2549 // SubRoute
2550 asyncResp->res.jsonValue["@odata.id"] =
2551 "/redfish/v1/Systems/system/LogServices/Crashdump";
2552 asyncResp->res.jsonValue["@odata.type"] =
2553 "#LogService.v1_2_0.LogService";
2554 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2555 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2556 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2557 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2558 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302559
Ed Tanous002d39b2022-05-31 08:59:27 -07002560 std::pair<std::string, std::string> redfishDateTimeOffset =
2561 crow::utility::getDateTimeOffsetNow();
2562 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2563 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2564 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302565
Ed Tanous002d39b2022-05-31 08:59:27 -07002566 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2567 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2568 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2569 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2570 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2571 ["target"] =
2572 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002573 });
2574}
2575
2576void inline requestRoutesCrashdumpClear(App& app)
2577{
George Liu0fda0f12021-11-16 10:06:17 +08002578 BMCWEB_ROUTE(
2579 app,
2580 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002581 // This is incorrect, should be:
2582 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002583 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002584 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002585 [&app](const crow::Request& req,
2586 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002587 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2588 {
2589 return;
2590 }
2591 crow::connections::systemBus->async_method_call(
2592 [asyncResp](const boost::system::error_code ec,
2593 const std::string&) {
2594 if (ec)
2595 {
2596 messages::internalError(asyncResp->res);
2597 return;
2598 }
2599 messages::success(asyncResp->res);
2600 },
2601 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2602 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002603}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002604
zhanghch058d1b46d2021-04-01 11:18:24 +08002605static void
2606 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2607 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002608{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002609 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002610 [asyncResp, logID,
2611 &logEntryJson](const boost::system::error_code ec,
2612 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002613 if (ec)
2614 {
2615 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2616 if (ec.value() ==
2617 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002618 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002619 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002620 }
2621 else
2622 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002623 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002624 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002625 return;
2626 }
2627
2628 std::string timestamp{};
2629 std::string filename{};
2630 std::string logfile{};
2631 parseCrashdumpParameters(params, filename, timestamp, logfile);
2632
2633 if (filename.empty() || timestamp.empty())
2634 {
2635 messages::resourceMissingAtURI(asyncResp->res,
2636 crow::utility::urlFromPieces(logID));
2637 return;
2638 }
2639
2640 std::string crashdumpURI =
2641 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2642 logID + "/" + filename;
2643 nlohmann::json logEntry = {
2644 {"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
2645 {"@odata.id",
2646 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2647 logID},
2648 {"Name", "CPU Crashdump"},
2649 {"Id", logID},
2650 {"EntryType", "Oem"},
2651 {"AdditionalDataURI", std::move(crashdumpURI)},
2652 {"DiagnosticDataType", "OEM"},
2653 {"OEMDiagnosticDataType", "PECICrashdump"},
2654 {"Created", std::move(timestamp)}};
2655
2656 // If logEntryJson references an array of LogEntry resources
2657 // ('Members' list), then push this as a new entry, otherwise set it
2658 // directly
2659 if (logEntryJson.is_array())
2660 {
2661 logEntryJson.push_back(logEntry);
2662 asyncResp->res.jsonValue["Members@odata.count"] =
2663 logEntryJson.size();
2664 }
2665 else
2666 {
2667 logEntryJson = logEntry;
2668 }
2669 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002670 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002671 std::move(getStoredLogCallback), crashdumpObject,
2672 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002673 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002674}
2675
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002676inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002677{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002678 // Note: Deviated from redfish privilege registry for GET & HEAD
2679 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002680 /**
2681 * Functions triggers appropriate requests on DBus
2682 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002683 BMCWEB_ROUTE(app,
2684 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002685 // This is incorrect, should be.
2686 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002687 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002688 .methods(boost::beast::http::verb::get)(
2689 [&app](const crow::Request& req,
2690 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2691 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2692 {
2693 return;
2694 }
2695 crow::connections::systemBus->async_method_call(
2696 [asyncResp](const boost::system::error_code ec,
2697 const std::vector<std::string>& resp) {
2698 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002699 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002700 if (ec.value() !=
2701 boost::system::errc::no_such_file_or_directory)
2702 {
2703 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2704 << ec.message();
2705 messages::internalError(asyncResp->res);
2706 return;
2707 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002708 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002709 asyncResp->res.jsonValue["@odata.type"] =
2710 "#LogEntryCollection.LogEntryCollection";
2711 asyncResp->res.jsonValue["@odata.id"] =
2712 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2713 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2714 asyncResp->res.jsonValue["Description"] =
2715 "Collection of Crashdump Entries";
2716 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2717 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002718
Ed Tanous002d39b2022-05-31 08:59:27 -07002719 for (const std::string& path : resp)
2720 {
2721 const sdbusplus::message::object_path objPath(path);
2722 // Get the log ID
2723 std::string logID = objPath.filename();
2724 if (logID.empty())
2725 {
2726 continue;
2727 }
2728 // Add the log entry to the array
2729 logCrashdumpEntry(asyncResp, logID,
2730 asyncResp->res.jsonValue["Members"]);
2731 }
2732 },
2733 "xyz.openbmc_project.ObjectMapper",
2734 "/xyz/openbmc_project/object_mapper",
2735 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2736 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002737 });
2738}
Ed Tanous1da66f72018-07-27 16:13:37 -07002739
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002740inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002741{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002742 // Note: Deviated from redfish privilege registry for GET & HEAD
2743 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002744
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002745 BMCWEB_ROUTE(
2746 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002747 // this is incorrect, should be
2748 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002749 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002750 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002751 [&app](const crow::Request& req,
2752 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2753 const std::string& param) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002754 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2755 {
2756 return;
2757 }
2758 const std::string& logID = param;
2759 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2760 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002761}
Ed Tanous1da66f72018-07-27 16:13:37 -07002762
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002763inline void requestRoutesCrashdumpFile(App& app)
2764{
2765 // Note: Deviated from redfish privilege registry for GET & HEAD
2766 // method for security reasons.
2767 BMCWEB_ROUTE(
2768 app,
2769 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002770 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002771 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002772 [&app](const crow::Request& req,
2773 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2774 const std::string& logID, const std::string& fileName) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002775 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2776 {
2777 return;
2778 }
2779 auto getStoredLogCallback =
2780 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2781 const boost::system::error_code ec,
2782 const std::vector<
2783 std::pair<std::string, dbus::utility::DbusVariantType>>&
2784 resp) {
2785 if (ec)
2786 {
2787 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2788 messages::internalError(asyncResp->res);
2789 return;
2790 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002791
Ed Tanous002d39b2022-05-31 08:59:27 -07002792 std::string dbusFilename{};
2793 std::string dbusTimestamp{};
2794 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002795
Ed Tanous002d39b2022-05-31 08:59:27 -07002796 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2797 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002798
Ed Tanous002d39b2022-05-31 08:59:27 -07002799 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2800 dbusFilepath.empty())
2801 {
2802 messages::resourceMissingAtURI(asyncResp->res, url);
2803 return;
2804 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002805
Ed Tanous002d39b2022-05-31 08:59:27 -07002806 // Verify the file name parameter is correct
2807 if (fileName != dbusFilename)
2808 {
2809 messages::resourceMissingAtURI(asyncResp->res, url);
2810 return;
2811 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002812
Ed Tanous002d39b2022-05-31 08:59:27 -07002813 if (!std::filesystem::exists(dbusFilepath))
2814 {
2815 messages::resourceMissingAtURI(asyncResp->res, url);
2816 return;
2817 }
2818 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2819 asyncResp->res.body() =
2820 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002821
Ed Tanous002d39b2022-05-31 08:59:27 -07002822 // Configure this to be a file download when accessed
2823 // from a browser
2824 asyncResp->res.addHeader("Content-Disposition", "attachment");
2825 };
2826 crow::connections::systemBus->async_method_call(
2827 std::move(getStoredLogCallback), crashdumpObject,
2828 crashdumpPath + std::string("/") + logID,
2829 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2830 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002831}
2832
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002833enum class OEMDiagnosticType
2834{
2835 onDemand,
2836 telemetry,
2837 invalid,
2838};
2839
Ed Tanousf7725d72022-03-07 12:46:00 -08002840inline OEMDiagnosticType
2841 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002842{
2843 if (oemDiagStr == "OnDemand")
2844 {
2845 return OEMDiagnosticType::onDemand;
2846 }
2847 if (oemDiagStr == "Telemetry")
2848 {
2849 return OEMDiagnosticType::telemetry;
2850 }
2851
2852 return OEMDiagnosticType::invalid;
2853}
2854
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002855inline void requestRoutesCrashdumpCollect(App& app)
2856{
2857 // Note: Deviated from redfish privilege registry for GET & HEAD
2858 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002859 BMCWEB_ROUTE(
2860 app,
2861 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002862 // The below is incorrect; Should be ConfigureManager
2863 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002864 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002865 .methods(boost::beast::http::verb::post)(
2866 [&app](const crow::Request& req,
2867 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2868 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2869 {
2870 return;
2871 }
2872 std::string diagnosticDataType;
2873 std::string oemDiagnosticDataType;
2874 if (!redfish::json_util::readJsonAction(
2875 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
2876 "OEMDiagnosticDataType", oemDiagnosticDataType))
2877 {
2878 return;
2879 }
2880
2881 if (diagnosticDataType != "OEM")
2882 {
2883 BMCWEB_LOG_ERROR
2884 << "Only OEM DiagnosticDataType supported for Crashdump";
2885 messages::actionParameterValueFormatError(
2886 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2887 "CollectDiagnosticData");
2888 return;
2889 }
2890
2891 OEMDiagnosticType oemDiagType =
2892 getOEMDiagnosticType(oemDiagnosticDataType);
2893
2894 std::string iface;
2895 std::string method;
2896 std::string taskMatchStr;
2897 if (oemDiagType == OEMDiagnosticType::onDemand)
2898 {
2899 iface = crashdumpOnDemandInterface;
2900 method = "GenerateOnDemandLog";
2901 taskMatchStr = "type='signal',"
2902 "interface='org.freedesktop.DBus.Properties',"
2903 "member='PropertiesChanged',"
2904 "arg0namespace='com.intel.crashdump'";
2905 }
2906 else if (oemDiagType == OEMDiagnosticType::telemetry)
2907 {
2908 iface = crashdumpTelemetryInterface;
2909 method = "GenerateTelemetryLog";
2910 taskMatchStr = "type='signal',"
2911 "interface='org.freedesktop.DBus.Properties',"
2912 "member='PropertiesChanged',"
2913 "arg0namespace='com.intel.crashdump'";
2914 }
2915 else
2916 {
2917 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2918 << oemDiagnosticDataType;
2919 messages::actionParameterValueFormatError(
2920 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
2921 "CollectDiagnosticData");
2922 return;
2923 }
2924
2925 auto collectCrashdumpCallback =
2926 [asyncResp, payload(task::Payload(req)),
2927 taskMatchStr](const boost::system::error_code ec,
2928 const std::string&) mutable {
2929 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002930 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002931 if (ec.value() == boost::system::errc::operation_not_supported)
2932 {
2933 messages::resourceInStandby(asyncResp->res);
2934 }
2935 else if (ec.value() ==
2936 boost::system::errc::device_or_resource_busy)
2937 {
2938 messages::serviceTemporarilyUnavailable(asyncResp->res,
2939 "60");
2940 }
2941 else
2942 {
2943 messages::internalError(asyncResp->res);
2944 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002945 return;
2946 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002947 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2948 [](boost::system::error_code err, sdbusplus::message::message&,
2949 const std::shared_ptr<task::TaskData>& taskData) {
2950 if (!err)
2951 {
2952 taskData->messages.emplace_back(messages::taskCompletedOK(
2953 std::to_string(taskData->index)));
2954 taskData->state = "Completed";
2955 }
2956 return task::completed;
2957 },
2958 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07002959
Ed Tanous002d39b2022-05-31 08:59:27 -07002960 task->startTimer(std::chrono::minutes(5));
2961 task->populateResp(asyncResp->res);
2962 task->payload.emplace(std::move(payload));
2963 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002964
Ed Tanous002d39b2022-05-31 08:59:27 -07002965 crow::connections::systemBus->async_method_call(
2966 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
2967 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002968 });
2969}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002970
Andrew Geisslercb92c032018-08-17 07:56:14 -07002971/**
2972 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2973 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002974inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002975{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002976 /**
2977 * Function handles POST method request.
2978 * The Clear Log actions does not require any parameter.The action deletes
2979 * all entries found in the Entries collection for this Log Service.
2980 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002981
George Liu0fda0f12021-11-16 10:06:17 +08002982 BMCWEB_ROUTE(
2983 app,
2984 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002985 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002986 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002987 [&app](const crow::Request& req,
2988 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002989 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
2990 {
2991 return;
2992 }
2993 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07002994
Ed Tanous002d39b2022-05-31 08:59:27 -07002995 // Process response from Logging service.
2996 auto respHandler = [asyncResp](const boost::system::error_code ec) {
2997 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2998 if (ec)
2999 {
3000 // TODO Handle for specific error code
3001 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3002 asyncResp->res.result(
3003 boost::beast::http::status::internal_server_error);
3004 return;
3005 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003006
Ed Tanous002d39b2022-05-31 08:59:27 -07003007 asyncResp->res.result(boost::beast::http::status::no_content);
3008 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003009
Ed Tanous002d39b2022-05-31 08:59:27 -07003010 // Make call to Logging service to request Clear Log
3011 crow::connections::systemBus->async_method_call(
3012 respHandler, "xyz.openbmc_project.Logging",
3013 "/xyz/openbmc_project/logging",
3014 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3015 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003016}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003017
3018/****************************************************
3019 * Redfish PostCode interfaces
3020 * using DBUS interface: getPostCodesTS
3021 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003022inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003023{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003024 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003025 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003026 .methods(boost::beast::http::verb::get)(
3027 [&app](const crow::Request& req,
3028 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3029 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3030 {
3031 return;
3032 }
Ed Tanous14766872022-03-15 10:44:42 -07003033
Ed Tanous002d39b2022-05-31 08:59:27 -07003034 asyncResp->res.jsonValue["@odata.id"] =
3035 "/redfish/v1/Systems/system/LogServices/PostCodes";
3036 asyncResp->res.jsonValue["@odata.type"] =
3037 "#LogService.v1_1_0.LogService";
3038 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3039 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3040 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3041 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3042 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3043 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303044
Ed Tanous002d39b2022-05-31 08:59:27 -07003045 std::pair<std::string, std::string> redfishDateTimeOffset =
3046 crow::utility::getDateTimeOffsetNow();
3047 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3048 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3049 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303050
Ed Tanous002d39b2022-05-31 08:59:27 -07003051 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3052 {"target",
3053 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003054 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003055}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003056
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003057inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003058{
George Liu0fda0f12021-11-16 10:06:17 +08003059 BMCWEB_ROUTE(
3060 app,
3061 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003062 // The following privilege is incorrect; It should be ConfigureManager
3063 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003064 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003065 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003066 [&app](const crow::Request& req,
3067 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003068 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3069 {
3070 return;
3071 }
3072 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003073
Ed Tanous002d39b2022-05-31 08:59:27 -07003074 // Make call to post-code service to request clear all
3075 crow::connections::systemBus->async_method_call(
3076 [asyncResp](const boost::system::error_code ec) {
3077 if (ec)
3078 {
3079 // TODO Handle for specific error code
3080 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3081 << ec;
3082 asyncResp->res.result(
3083 boost::beast::http::status::internal_server_error);
3084 messages::internalError(asyncResp->res);
3085 return;
3086 }
3087 },
3088 "xyz.openbmc_project.State.Boot.PostCode0",
3089 "/xyz/openbmc_project/State/Boot/PostCode0",
3090 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3091 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003092}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003093
3094static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003095 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303096 const boost::container::flat_map<
3097 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003098 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3099 const uint64_t skip = 0, const uint64_t top = 0)
3100{
3101 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003102 const registries::Message* message =
3103 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003104
3105 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003106 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003107
3108 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303109 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3110 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003111 {
3112 currentCodeIndex++;
3113 std::string postcodeEntryID =
3114 "B" + std::to_string(bootIndex) + "-" +
3115 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3116
3117 uint64_t usecSinceEpoch = code.first;
3118 uint64_t usTimeOffset = 0;
3119
3120 if (1 == currentCodeIndex)
3121 { // already incremented
3122 firstCodeTimeUs = code.first;
3123 }
3124 else
3125 {
3126 usTimeOffset = code.first - firstCodeTimeUs;
3127 }
3128
3129 // skip if no specific codeIndex is specified and currentCodeIndex does
3130 // not fall between top and skip
3131 if ((codeIndex == 0) &&
3132 (currentCodeIndex <= skip || currentCodeIndex > top))
3133 {
3134 continue;
3135 }
3136
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003137 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003138 // currentIndex
3139 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3140 {
3141 // This is done for simplicity. 1st entry is needed to calculate
3142 // time offset. To improve efficiency, one can get to the entry
3143 // directly (possibly with flatmap's nth method)
3144 continue;
3145 }
3146
3147 // currentCodeIndex is within top and skip or equal to specified code
3148 // index
3149
3150 // Get the Created time from the timestamp
3151 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003152 entryTimeStr =
3153 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003154
3155 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3156 std::ostringstream hexCode;
3157 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303158 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003159 std::ostringstream timeOffsetStr;
3160 // Set Fixed -Point Notation
3161 timeOffsetStr << std::fixed;
3162 // Set precision to 4 digits
3163 timeOffsetStr << std::setprecision(4);
3164 // Add double to stream
3165 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3166 std::vector<std::string> messageArgs = {
3167 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3168
3169 // Get MessageArgs template from message registry
3170 std::string msg;
3171 if (message != nullptr)
3172 {
3173 msg = message->message;
3174
3175 // fill in this post code value
3176 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003177 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003178 {
3179 std::string argStr = "%" + std::to_string(++i);
3180 size_t argPos = msg.find(argStr);
3181 if (argPos != std::string::npos)
3182 {
3183 msg.replace(argPos, argStr.length(), messageArg);
3184 }
3185 }
3186 }
3187
Tim Leed4342a92020-04-27 11:47:58 +08003188 // Get Severity template from message registry
3189 std::string severity;
3190 if (message != nullptr)
3191 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003192 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003193 }
3194
ZhikuiRena3316fc2020-01-29 14:58:08 -08003195 // add to AsyncResp
3196 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003197 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003198 bmcLogEntry = {
3199 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3200 {"@odata.id",
3201 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3202 postcodeEntryID},
3203 {"Name", "POST Code Log Entry"},
3204 {"Id", postcodeEntryID},
3205 {"Message", std::move(msg)},
3206 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3207 {"MessageArgs", std::move(messageArgs)},
3208 {"EntryType", "Event"},
3209 {"Severity", std::move(severity)},
3210 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003211 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3212 {
3213 bmcLogEntry["AdditionalDataURI"] =
3214 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3215 postcodeEntryID + "/attachment";
3216 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003217 }
3218}
3219
zhanghch058d1b46d2021-04-01 11:18:24 +08003220static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003221 const uint16_t bootIndex,
3222 const uint64_t codeIndex)
3223{
3224 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303225 [aResp, bootIndex,
3226 codeIndex](const boost::system::error_code ec,
3227 const boost::container::flat_map<
3228 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3229 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003230 if (ec)
3231 {
3232 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3233 messages::internalError(aResp->res);
3234 return;
3235 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003236
Ed Tanous002d39b2022-05-31 08:59:27 -07003237 // skip the empty postcode boots
3238 if (postcode.empty())
3239 {
3240 return;
3241 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003242
Ed Tanous002d39b2022-05-31 08:59:27 -07003243 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003244
Ed Tanous002d39b2022-05-31 08:59:27 -07003245 aResp->res.jsonValue["Members@odata.count"] =
3246 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003247 },
Jonathan Doman15124762021-01-07 17:54:17 -08003248 "xyz.openbmc_project.State.Boot.PostCode0",
3249 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003250 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3251 bootIndex);
3252}
3253
zhanghch058d1b46d2021-04-01 11:18:24 +08003254static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003255 const uint16_t bootIndex,
3256 const uint16_t bootCount,
3257 const uint64_t entryCount, const uint64_t skip,
3258 const uint64_t top)
3259{
3260 crow::connections::systemBus->async_method_call(
3261 [aResp, bootIndex, bootCount, entryCount, skip,
3262 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303263 const boost::container::flat_map<
3264 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3265 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003266 if (ec)
3267 {
3268 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3269 messages::internalError(aResp->res);
3270 return;
3271 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003272
Ed Tanous002d39b2022-05-31 08:59:27 -07003273 uint64_t endCount = entryCount;
3274 if (!postcode.empty())
3275 {
3276 endCount = entryCount + postcode.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003277
Ed Tanous002d39b2022-05-31 08:59:27 -07003278 if ((skip < endCount) && ((top + skip) > entryCount))
ZhikuiRena3316fc2020-01-29 14:58:08 -08003279 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003280 uint64_t thisBootSkip = std::max(skip, entryCount) - entryCount;
3281 uint64_t thisBootTop =
3282 std::min(top + skip, endCount) - entryCount;
3283
3284 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3285 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003286 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003287 aResp->res.jsonValue["Members@odata.count"] = endCount;
3288 }
3289
3290 // continue to previous bootIndex
3291 if (bootIndex < bootCount)
3292 {
3293 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3294 bootCount, endCount, skip, top);
3295 }
3296 else
3297 {
3298 aResp->res.jsonValue["Members@odata.nextLink"] =
3299 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3300 std::to_string(skip + top);
3301 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003302 },
Jonathan Doman15124762021-01-07 17:54:17 -08003303 "xyz.openbmc_project.State.Boot.PostCode0",
3304 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003305 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3306 bootIndex);
3307}
3308
zhanghch058d1b46d2021-04-01 11:18:24 +08003309static void
3310 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3311 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003312{
3313 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003314 sdbusplus::asio::getProperty<uint16_t>(
3315 *crow::connections::systemBus,
3316 "xyz.openbmc_project.State.Boot.PostCode0",
3317 "/xyz/openbmc_project/State/Boot/PostCode0",
3318 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3319 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3320 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003321 if (ec)
3322 {
3323 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3324 messages::internalError(aResp->res);
3325 return;
3326 }
3327 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003328 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003329}
3330
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003331inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003332{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003333 BMCWEB_ROUTE(app,
3334 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003335 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003336 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003337 [&app](const crow::Request& req,
3338 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003339 query_param::QueryCapabilities capabilities = {
3340 .canDelegateTop = true,
3341 .canDelegateSkip = true,
3342 };
3343 query_param::Query delegatedQuery;
3344 if (!redfish::setUpRedfishRouteWithDelegation(
3345 app, req, asyncResp->res, delegatedQuery, capabilities))
3346 {
3347 return;
3348 }
3349 asyncResp->res.jsonValue["@odata.type"] =
3350 "#LogEntryCollection.LogEntryCollection";
3351 asyncResp->res.jsonValue["@odata.id"] =
3352 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3353 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3354 asyncResp->res.jsonValue["Description"] =
3355 "Collection of POST Code Log Entries";
3356 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3357 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003358
Ed Tanous002d39b2022-05-31 08:59:27 -07003359 getCurrentBootNumber(asyncResp, delegatedQuery.skip,
3360 delegatedQuery.top);
3361 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003362}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003363
George Liu647b3cd2021-07-05 12:43:56 +08003364/**
3365 * @brief Parse post code ID and get the current value and index value
3366 * eg: postCodeID=B1-2, currentValue=1, index=2
3367 *
3368 * @param[in] postCodeID Post Code ID
3369 * @param[out] currentValue Current value
3370 * @param[out] index Index value
3371 *
3372 * @return bool true if the parsing is successful, false the parsing fails
3373 */
3374inline static bool parsePostCode(const std::string& postCodeID,
3375 uint64_t& currentValue, uint16_t& index)
3376{
3377 std::vector<std::string> split;
3378 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3379 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3380 {
3381 return false;
3382 }
3383
Ed Tanousca45aa32022-01-07 09:28:45 -08003384 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003385 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003386 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003387 const char* end = split[0].data() + split[0].size();
3388 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3389
3390 if (ptrIndex != end || ecIndex != std::errc())
3391 {
3392 return false;
3393 }
3394
3395 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003396
3397 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003398 end = split[1].data() + split[1].size();
3399 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003400
Ed Tanousdcf2ebc2022-01-25 10:07:45 -08003401 return ptrValue == end && ecValue != std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003402}
3403
3404inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3405{
George Liu0fda0f12021-11-16 10:06:17 +08003406 BMCWEB_ROUTE(
3407 app,
3408 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003409 .privileges(redfish::privileges::getLogEntry)
3410 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003411 [&app](const crow::Request& req,
3412 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3413 const std::string& postCodeID) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003414 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3415 {
3416 return;
3417 }
3418 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3419 {
3420 asyncResp->res.result(boost::beast::http::status::bad_request);
3421 return;
3422 }
George Liu647b3cd2021-07-05 12:43:56 +08003423
Ed Tanous002d39b2022-05-31 08:59:27 -07003424 uint64_t currentValue = 0;
3425 uint16_t index = 0;
3426 if (!parsePostCode(postCodeID, currentValue, index))
3427 {
3428 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3429 return;
3430 }
George Liu647b3cd2021-07-05 12:43:56 +08003431
Ed Tanous002d39b2022-05-31 08:59:27 -07003432 crow::connections::systemBus->async_method_call(
3433 [asyncResp, postCodeID, currentValue](
3434 const boost::system::error_code ec,
3435 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3436 postcodes) {
3437 if (ec.value() == EBADR)
3438 {
3439 messages::resourceNotFound(asyncResp->res, "LogEntry",
3440 postCodeID);
3441 return;
3442 }
3443 if (ec)
3444 {
3445 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3446 messages::internalError(asyncResp->res);
3447 return;
3448 }
George Liu647b3cd2021-07-05 12:43:56 +08003449
Ed Tanous002d39b2022-05-31 08:59:27 -07003450 size_t value = static_cast<size_t>(currentValue) - 1;
3451 if (value == std::string::npos || postcodes.size() < currentValue)
3452 {
3453 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3454 messages::resourceNotFound(asyncResp->res, "LogEntry",
3455 postCodeID);
3456 return;
3457 }
George Liu647b3cd2021-07-05 12:43:56 +08003458
Ed Tanous002d39b2022-05-31 08:59:27 -07003459 const auto& [tID, c] = postcodes[value];
3460 if (c.empty())
3461 {
3462 BMCWEB_LOG_INFO << "No found post code data";
3463 messages::resourceNotFound(asyncResp->res, "LogEntry",
3464 postCodeID);
3465 return;
3466 }
3467 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3468 const char* d = reinterpret_cast<const char*>(c.data());
3469 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003470
Ed Tanous002d39b2022-05-31 08:59:27 -07003471 asyncResp->res.addHeader("Content-Type",
3472 "application/octet-stream");
3473 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
3474 asyncResp->res.body() = crow::utility::base64encode(strData);
3475 },
3476 "xyz.openbmc_project.State.Boot.PostCode0",
3477 "/xyz/openbmc_project/State/Boot/PostCode0",
3478 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3479 });
George Liu647b3cd2021-07-05 12:43:56 +08003480}
3481
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003482inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003483{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003484 BMCWEB_ROUTE(
3485 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003486 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003487 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003488 [&app](const crow::Request& req,
3489 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3490 const std::string& targetID) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003491 if (!redfish::setUpRedfishRoute(app, req, asyncResp->res))
3492 {
3493 return;
3494 }
3495 uint16_t bootIndex = 0;
3496 uint64_t codeIndex = 0;
3497 if (!parsePostCode(targetID, codeIndex, bootIndex))
3498 {
3499 // Requested ID was not found
3500 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3501 return;
3502 }
3503 if (bootIndex == 0 || codeIndex == 0)
3504 {
3505 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3506 << targetID;
3507 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003508
Ed Tanous002d39b2022-05-31 08:59:27 -07003509 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3510 asyncResp->res.jsonValue["@odata.id"] =
3511 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3512 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3513 asyncResp->res.jsonValue["Description"] =
3514 "Collection of POST Code Log Entries";
3515 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3516 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003517
Ed Tanous002d39b2022-05-31 08:59:27 -07003518 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3519 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003520}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003521
Ed Tanous1da66f72018-07-27 16:13:37 -07003522} // namespace redfish