blob: 0a1686674ebc1b874a76cb70b1a5013350e2d8bb [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
Nan Zhou21ab4042022-06-26 23:07:40 +0000328static std::string getDumpEntriesPath(const std::string& dumpType)
Claire Weinanfdd26902022-03-01 14:18:25 -0800329{
330 std::string entriesPath;
331
332 if (dumpType == "BMC")
333 {
334 entriesPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
335 }
336 else if (dumpType == "FaultLog")
337 {
338 entriesPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/";
339 }
340 else if (dumpType == "System")
341 {
342 entriesPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
343 }
344 else
345 {
346 BMCWEB_LOG_ERROR << "getDumpEntriesPath() invalid dump type: "
347 << dumpType;
348 }
349
350 // Returns empty string on error
351 return entriesPath;
352}
353
zhanghch058d1b46d2021-04-01 11:18:24 +0800354inline void
355 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
356 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500357{
Claire Weinanfdd26902022-03-01 14:18:25 -0800358 std::string entriesPath = getDumpEntriesPath(dumpType);
359 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500360 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500361 messages::internalError(asyncResp->res);
362 return;
363 }
364
365 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800366 [asyncResp, entriesPath,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800367 dumpType](const boost::system::error_code ec,
368 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700369 if (ec)
370 {
371 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
372 messages::internalError(asyncResp->res);
373 return;
374 }
375
Claire Weinanfdd26902022-03-01 14:18:25 -0800376 // Remove ending slash
377 std::string odataIdStr = entriesPath;
378 if (!odataIdStr.empty())
379 {
380 odataIdStr.pop_back();
381 }
382
383 asyncResp->res.jsonValue["@odata.type"] =
384 "#LogEntryCollection.LogEntryCollection";
385 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
386 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
387 asyncResp->res.jsonValue["Description"] =
388 "Collection of " + dumpType + " Dump Entries";
389
Ed Tanous002d39b2022-05-31 08:59:27 -0700390 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
391 entriesArray = nlohmann::json::array();
392 std::string dumpEntryPath =
393 "/xyz/openbmc_project/dump/" +
394 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
395
396 std::sort(resp.begin(), resp.end(), [](const auto& l, const auto& r) {
397 return AlphanumLess<std::string>()(l.first.filename(),
398 r.first.filename());
399 });
400
401 for (auto& object : resp)
402 {
403 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500404 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700405 continue;
406 }
407 uint64_t timestamp = 0;
408 uint64_t size = 0;
409 std::string dumpStatus;
410 nlohmann::json thisEntry;
411
412 std::string entryID = object.first.filename();
413 if (entryID.empty())
414 {
415 continue;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500416 }
417
Ed Tanous002d39b2022-05-31 08:59:27 -0700418 for (auto& interfaceMap : object.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500419 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700420 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500421 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700422 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500423 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700424 if (propertyMap.first == "Status")
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500425 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700426 const auto* status =
427 std::get_if<std::string>(&propertyMap.second);
428 if (status == nullptr)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500429 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700430 messages::internalError(asyncResp->res);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500431 break;
432 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700433 dumpStatus = *status;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500434 }
435 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700436 }
437 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
438 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500439
Ed Tanous002d39b2022-05-31 08:59:27 -0700440 for (auto& propertyMap : interfaceMap.second)
441 {
442 if (propertyMap.first == "Size")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500443 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700444 const auto* sizePtr =
445 std::get_if<uint64_t>(&propertyMap.second);
446 if (sizePtr == nullptr)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500447 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700448 messages::internalError(asyncResp->res);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500449 break;
450 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700451 size = *sizePtr;
452 break;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500453 }
454 }
455 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 else if (interfaceMap.first ==
457 "xyz.openbmc_project.Time.EpochTime")
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500458 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500459
Ed Tanous002d39b2022-05-31 08:59:27 -0700460 for (const auto& propertyMap : interfaceMap.second)
461 {
462 if (propertyMap.first == "Elapsed")
463 {
464 const uint64_t* usecsTimeStamp =
465 std::get_if<uint64_t>(&propertyMap.second);
466 if (usecsTimeStamp == nullptr)
467 {
468 messages::internalError(asyncResp->res);
469 break;
470 }
471 timestamp = (*usecsTimeStamp / 1000 / 1000);
472 break;
473 }
474 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500475 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500476 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700477
478 if (dumpStatus !=
479 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
480 !dumpStatus.empty())
481 {
482 // Dump status is not Complete, no need to enumerate
483 continue;
484 }
485
486 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800487 thisEntry["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700488 thisEntry["Id"] = entryID;
489 thisEntry["EntryType"] = "Event";
490 thisEntry["Created"] = crow::utility::getDateTimeUint(timestamp);
491 thisEntry["Name"] = dumpType + " Dump Entry";
492
Ed Tanous002d39b2022-05-31 08:59:27 -0700493 if (dumpType == "BMC")
494 {
495 thisEntry["DiagnosticDataType"] = "Manager";
496 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800497 entriesPath + entryID + "/attachment";
498 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700499 }
500 else if (dumpType == "System")
501 {
502 thisEntry["DiagnosticDataType"] = "OEM";
503 thisEntry["OEMDiagnosticDataType"] = "System";
504 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800505 entriesPath + entryID + "/attachment";
506 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700507 }
508 entriesArray.push_back(std::move(thisEntry));
509 }
510 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500511 },
512 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
513 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
514}
515
zhanghch058d1b46d2021-04-01 11:18:24 +0800516inline void
Claire Weinanc7a6d662022-06-13 16:36:39 -0700517 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800518 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500519{
Claire Weinanfdd26902022-03-01 14:18:25 -0800520 std::string entriesPath = getDumpEntriesPath(dumpType);
521 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500522 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500523 messages::internalError(asyncResp->res);
524 return;
525 }
526
527 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800528 [asyncResp, entryID, dumpType,
529 entriesPath](const boost::system::error_code ec,
530 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700531 if (ec)
532 {
533 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
534 messages::internalError(asyncResp->res);
535 return;
536 }
537
538 bool foundDumpEntry = false;
539 std::string dumpEntryPath =
540 "/xyz/openbmc_project/dump/" +
541 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
542
543 for (const auto& objectPath : resp)
544 {
545 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500546 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700547 continue;
548 }
549
550 foundDumpEntry = true;
551 uint64_t timestamp = 0;
552 uint64_t size = 0;
553 std::string dumpStatus;
554
555 for (const auto& interfaceMap : objectPath.second)
556 {
557 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
558 {
559 for (const auto& propertyMap : interfaceMap.second)
560 {
561 if (propertyMap.first == "Status")
562 {
563 const std::string* status =
564 std::get_if<std::string>(&propertyMap.second);
565 if (status == nullptr)
566 {
567 messages::internalError(asyncResp->res);
568 break;
569 }
570 dumpStatus = *status;
571 }
572 }
573 }
574 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
575 {
576 for (const auto& propertyMap : interfaceMap.second)
577 {
578 if (propertyMap.first == "Size")
579 {
580 const uint64_t* sizePtr =
581 std::get_if<uint64_t>(&propertyMap.second);
582 if (sizePtr == nullptr)
583 {
584 messages::internalError(asyncResp->res);
585 break;
586 }
587 size = *sizePtr;
588 break;
589 }
590 }
591 }
592 else if (interfaceMap.first ==
593 "xyz.openbmc_project.Time.EpochTime")
594 {
595 for (const auto& propertyMap : interfaceMap.second)
596 {
597 if (propertyMap.first == "Elapsed")
598 {
599 const uint64_t* usecsTimeStamp =
600 std::get_if<uint64_t>(&propertyMap.second);
601 if (usecsTimeStamp == nullptr)
602 {
603 messages::internalError(asyncResp->res);
604 break;
605 }
606 timestamp = *usecsTimeStamp / 1000 / 1000;
607 break;
608 }
609 }
610 }
611 }
612
613 if (dumpStatus !=
614 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
615 !dumpStatus.empty())
616 {
617 // Dump status is not Complete
618 // return not found until status is changed to Completed
619 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
620 entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500621 return;
622 }
623
Ed Tanous002d39b2022-05-31 08:59:27 -0700624 asyncResp->res.jsonValue["@odata.type"] =
625 "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800626 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700627 asyncResp->res.jsonValue["Id"] = entryID;
628 asyncResp->res.jsonValue["EntryType"] = "Event";
629 asyncResp->res.jsonValue["Created"] =
630 crow::utility::getDateTimeUint(timestamp);
631 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500632
Ed Tanous002d39b2022-05-31 08:59:27 -0700633 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500634 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700635 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
636 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800637 entriesPath + entryID + "/attachment";
638 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500639 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700640 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500641 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700642 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
643 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
644 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800645 entriesPath + entryID + "/attachment";
646 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500647 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700648 }
649 if (!foundDumpEntry)
650 {
651 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
652 messages::internalError(asyncResp->res);
653 return;
654 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500655 },
656 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
657 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
658}
659
zhanghch058d1b46d2021-04-01 11:18:24 +0800660inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800661 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500662 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500663{
Ed Tanous002d39b2022-05-31 08:59:27 -0700664 auto respHandler =
665 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500666 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
667 if (ec)
668 {
George Liu3de8d8b2021-03-22 17:49:39 +0800669 if (ec.value() == EBADR)
670 {
671 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
672 return;
673 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500674 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -0800675 << ec << " entryID=" << entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500676 messages::internalError(asyncResp->res);
677 return;
678 }
679 };
680 crow::connections::systemBus->async_method_call(
681 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500682 "/xyz/openbmc_project/dump/" +
683 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
684 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500685 "xyz.openbmc_project.Object.Delete", "Delete");
686}
687
zhanghch058d1b46d2021-04-01 11:18:24 +0800688inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700689 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800690 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
691 const uint32_t& dumpId, const std::string& dumpPath,
692 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500693{
694 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500695 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500696 boost::system::error_code err, sdbusplus::message::message& m,
697 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700698 if (err)
699 {
700 BMCWEB_LOG_ERROR << "Error in creating a dump";
701 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500702 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700703 }
704
705 dbus::utility::DBusInteracesMap interfacesList;
706
707 sdbusplus::message::object_path objPath;
708
709 m.read(objPath, interfacesList);
710
711 if (objPath.str ==
712 "/xyz/openbmc_project/dump/" +
713 std::string(boost::algorithm::to_lower_copy(dumpType)) +
714 "/entry/" + std::to_string(dumpId))
715 {
716 nlohmann::json retMessage = messages::success();
717 taskData->messages.emplace_back(retMessage);
718
719 std::string headerLoc =
720 "Location: " + dumpPath + std::to_string(dumpId);
721 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
722
723 taskData->state = "Completed";
724 return task::completed;
725 }
726 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500727 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800728 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500729 "member='InterfacesAdded', "
730 "path='/xyz/openbmc_project/dump'");
731
732 task->startTimer(std::chrono::minutes(3));
733 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700734 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500735}
736
zhanghch058d1b46d2021-04-01 11:18:24 +0800737inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
738 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500739{
Claire Weinanfdd26902022-03-01 14:18:25 -0800740 std::string dumpPath = getDumpEntriesPath(dumpType);
741 if (dumpPath.empty())
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500742 {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500743 messages::internalError(asyncResp->res);
744 return;
745 }
746
747 std::optional<std::string> diagnosticDataType;
748 std::optional<std::string> oemDiagnosticDataType;
749
Willy Tu15ed6782021-12-14 11:03:16 -0800750 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500751 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
752 "OEMDiagnosticDataType", oemDiagnosticDataType))
753 {
754 return;
755 }
756
757 if (dumpType == "System")
758 {
759 if (!oemDiagnosticDataType || !diagnosticDataType)
760 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800761 BMCWEB_LOG_ERROR
762 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500763 messages::actionParameterMissing(
764 asyncResp->res, "CollectDiagnosticData",
765 "DiagnosticDataType & OEMDiagnosticDataType");
766 return;
767 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700768 if ((*oemDiagnosticDataType != "System") ||
769 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500770 {
771 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700772 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500773 return;
774 }
775 }
776 else if (dumpType == "BMC")
777 {
778 if (!diagnosticDataType)
779 {
George Liu0fda0f12021-11-16 10:06:17 +0800780 BMCWEB_LOG_ERROR
781 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500782 messages::actionParameterMissing(
783 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
784 return;
785 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700786 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500787 {
788 BMCWEB_LOG_ERROR
789 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700790 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500791 return;
792 }
793 }
794
795 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700796 [asyncResp, payload(task::Payload(req)), dumpPath,
797 dumpType](const boost::system::error_code ec,
798 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700799 if (ec)
800 {
801 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
802 messages::internalError(asyncResp->res);
803 return;
804 }
805 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500806
Ed Tanous002d39b2022-05-31 08:59:27 -0700807 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
808 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500809 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500810 "xyz.openbmc_project.Dump.Manager",
811 "/xyz/openbmc_project/dump/" +
812 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500813 "xyz.openbmc_project.Dump.Create", "CreateDump");
814}
815
zhanghch058d1b46d2021-04-01 11:18:24 +0800816inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
817 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500818{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500819 std::string dumpTypeLowerCopy =
820 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800821
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500822 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800823 [asyncResp, dumpType](
824 const boost::system::error_code ec,
825 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700826 if (ec)
827 {
828 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
829 messages::internalError(asyncResp->res);
830 return;
831 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500832
Ed Tanous002d39b2022-05-31 08:59:27 -0700833 for (const std::string& path : subTreePaths)
834 {
835 sdbusplus::message::object_path objPath(path);
836 std::string logID = objPath.filename();
837 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500838 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700839 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500840 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700841 deleteDumpEntry(asyncResp, logID, dumpType);
842 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500843 },
844 "xyz.openbmc_project.ObjectMapper",
845 "/xyz/openbmc_project/object_mapper",
846 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500847 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
848 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
849 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500850}
851
Ed Tanousb9d36b42022-02-26 21:42:46 -0800852inline static void
853 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
854 std::string& filename, std::string& timestamp,
855 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700856{
857 for (auto property : params)
858 {
859 if (property.first == "Timestamp")
860 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500861 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500862 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700863 if (value != nullptr)
864 {
865 timestamp = *value;
866 }
867 }
868 else if (property.first == "Filename")
869 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500870 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500871 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700872 if (value != nullptr)
873 {
874 filename = *value;
875 }
876 }
877 else if (property.first == "Log")
878 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500879 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500880 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700881 if (value != nullptr)
882 {
883 logfile = *value;
884 }
885 }
886 }
887}
888
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500889constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700890inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700891{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800892 /**
893 * Functions triggers appropriate requests on DBus
894 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700895 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700896 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700897 .methods(boost::beast::http::verb::get)(
898 [&app](const crow::Request& req,
899 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000900 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700901 {
902 return;
903 }
904 // Collections don't include the static data added by SubRoute
905 // because it has a duplicate entry for members
906 asyncResp->res.jsonValue["@odata.type"] =
907 "#LogServiceCollection.LogServiceCollection";
908 asyncResp->res.jsonValue["@odata.id"] =
909 "/redfish/v1/Systems/system/LogServices";
910 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
911 asyncResp->res.jsonValue["Description"] =
912 "Collection of LogServices for this Computer System";
913 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
914 logServiceArray = nlohmann::json::array();
915 nlohmann::json::object_t eventLog;
916 eventLog["@odata.id"] =
917 "/redfish/v1/Systems/system/LogServices/EventLog";
918 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500919#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700920 nlohmann::json::object_t dumpLog;
921 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
922 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600923#endif
924
Jason M. Billsd53dd412019-02-12 17:16:22 -0800925#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700926 nlohmann::json::object_t crashdump;
927 crashdump["@odata.id"] =
928 "/redfish/v1/Systems/system/LogServices/Crashdump";
929 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800930#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800931
932#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700933 nlohmann::json::object_t hostlogger;
934 hostlogger["@odata.id"] =
935 "/redfish/v1/Systems/system/LogServices/HostLogger";
936 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800937#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700938 asyncResp->res.jsonValue["Members@odata.count"] =
939 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800940
Ed Tanous002d39b2022-05-31 08:59:27 -0700941 crow::connections::systemBus->async_method_call(
942 [asyncResp](const boost::system::error_code ec,
943 const dbus::utility::MapperGetSubTreePathsResponse&
944 subtreePath) {
945 if (ec)
946 {
947 BMCWEB_LOG_ERROR << ec;
948 return;
949 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700950
Ed Tanous002d39b2022-05-31 08:59:27 -0700951 for (const auto& pathStr : subtreePath)
952 {
953 if (pathStr.find("PostCode") != std::string::npos)
954 {
955 nlohmann::json& logServiceArrayLocal =
956 asyncResp->res.jsonValue["Members"];
957 logServiceArrayLocal.push_back(
958 {{"@odata.id",
959 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
960 asyncResp->res.jsonValue["Members@odata.count"] =
961 logServiceArrayLocal.size();
962 return;
963 }
964 }
965 },
966 "xyz.openbmc_project.ObjectMapper",
967 "/xyz/openbmc_project/object_mapper",
968 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
969 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700970 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971}
972
973inline void requestRoutesEventLogService(App& app)
974{
975 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700976 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700977 .methods(boost::beast::http::verb::get)(
978 [&app](const crow::Request& req,
979 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000980 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700981 {
982 return;
983 }
984 asyncResp->res.jsonValue["@odata.id"] =
985 "/redfish/v1/Systems/system/LogServices/EventLog";
986 asyncResp->res.jsonValue["@odata.type"] =
987 "#LogService.v1_1_0.LogService";
988 asyncResp->res.jsonValue["Name"] = "Event Log Service";
989 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
990 asyncResp->res.jsonValue["Id"] = "EventLog";
991 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530992
Ed Tanous002d39b2022-05-31 08:59:27 -0700993 std::pair<std::string, std::string> redfishDateTimeOffset =
994 crow::utility::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530995
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
997 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
998 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +0530999
Ed Tanous002d39b2022-05-31 08:59:27 -07001000 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1001 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1002 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001003
Ed Tanous002d39b2022-05-31 08:59:27 -07001004 {"target",
1005 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001006 });
1007}
1008
1009inline void requestRoutesJournalEventLogClear(App& app)
1010{
Jason M. Bills4978b632022-02-22 14:17:43 -08001011 BMCWEB_ROUTE(
1012 app,
1013 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001014 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001015 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001016 [&app](const crow::Request& req,
1017 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001018 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001019 {
1020 return;
1021 }
1022 // Clear the EventLog by deleting the log files
1023 std::vector<std::filesystem::path> redfishLogFiles;
1024 if (getRedfishLogFiles(redfishLogFiles))
1025 {
1026 for (const std::filesystem::path& file : redfishLogFiles)
1027 {
1028 std::error_code ec;
1029 std::filesystem::remove(file, ec);
1030 }
1031 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001032
Ed Tanous002d39b2022-05-31 08:59:27 -07001033 // Reload rsyslog so it knows to start new log files
1034 crow::connections::systemBus->async_method_call(
1035 [asyncResp](const boost::system::error_code ec) {
1036 if (ec)
1037 {
1038 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1039 messages::internalError(asyncResp->res);
1040 return;
1041 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001042
Ed Tanous002d39b2022-05-31 08:59:27 -07001043 messages::success(asyncResp->res);
1044 },
1045 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1046 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1047 "replace");
1048 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001049}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001050
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001051static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001052 const std::string& logEntry,
Jason M. Billsde703c52022-06-23 14:19:04 -07001053 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001054{
Jason M. Bills95820182019-04-22 16:25:34 -07001055 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001056 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001057 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001058 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001059 {
1060 return 1;
1061 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001062 std::string timestamp = logEntry.substr(0, space);
1063 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001064 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001065 if (entryStart == std::string::npos)
1066 {
1067 return 1;
1068 }
1069 std::string_view entry(logEntry);
1070 entry.remove_prefix(entryStart);
1071 // Use split to separate the entry into its fields
1072 std::vector<std::string> logEntryFields;
1073 boost::split(logEntryFields, entry, boost::is_any_of(","),
1074 boost::token_compress_on);
1075 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001076 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001077 {
1078 return 1;
1079 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001080 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001081
Jason M. Bills4851d452019-03-28 11:27:48 -07001082 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001083 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001084
Sui Chen54417b02022-03-24 14:59:52 -07001085 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001086 {
Sui Chen54417b02022-03-24 14:59:52 -07001087 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
1088 return 0;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001089 }
1090
Sui Chen54417b02022-03-24 14:59:52 -07001091 std::string msg = message->message;
1092
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001093 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001094 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001095 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001096 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001097 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001098 // If the first string is empty, assume there are no MessageArgs
1099 std::size_t messageArgsSize = 0;
1100 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001101 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001102 messageArgsSize = logEntryFields.size() - 1;
1103 }
1104
Ed Tanous23a21a12020-07-25 04:45:05 +00001105 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001106
1107 // Fill the MessageArgs into the Message
1108 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001109 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001110 {
1111 std::string argStr = "%" + std::to_string(++i);
1112 size_t argPos = msg.find(argStr);
1113 if (argPos != std::string::npos)
1114 {
1115 msg.replace(argPos, argStr.length(), messageArg);
1116 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001117 }
1118 }
1119
Jason M. Bills95820182019-04-22 16:25:34 -07001120 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1121 // format which matches the Redfish format except for the fractional seconds
1122 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001123 std::size_t dot = timestamp.find_first_of('.');
1124 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001125 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001126 {
Jason M. Bills95820182019-04-22 16:25:34 -07001127 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001128 }
1129
1130 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001131 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001132 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001133 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001134 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001135 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001136 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001137 {"Id", logEntryID},
1138 {"Message", std::move(msg)},
1139 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001140 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001141 {"EntryType", "Event"},
Sui Chen54417b02022-03-24 14:59:52 -07001142 {"Severity", message->messageSeverity},
Jason M. Bills95820182019-04-22 16:25:34 -07001143 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001144 return 0;
1145}
1146
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001147inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001148{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001149 BMCWEB_ROUTE(app,
1150 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001151 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001152 .methods(boost::beast::http::verb::get)(
1153 [&app](const crow::Request& req,
1154 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1155 query_param::QueryCapabilities capabilities = {
1156 .canDelegateTop = true,
1157 .canDelegateSkip = true,
1158 };
1159 query_param::Query delegatedQuery;
1160 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001161 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001162 {
1163 return;
1164 }
1165 // Collections don't include the static data added by SubRoute
1166 // because it has a duplicate entry for members
1167 asyncResp->res.jsonValue["@odata.type"] =
1168 "#LogEntryCollection.LogEntryCollection";
1169 asyncResp->res.jsonValue["@odata.id"] =
1170 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1171 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1172 asyncResp->res.jsonValue["Description"] =
1173 "Collection of System Event Log Entries";
1174
1175 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1176 logEntryArray = nlohmann::json::array();
1177 // Go through the log files and create a unique ID for each
1178 // entry
1179 std::vector<std::filesystem::path> redfishLogFiles;
1180 getRedfishLogFiles(redfishLogFiles);
1181 uint64_t entryCount = 0;
1182 std::string logEntry;
1183
1184 // Oldest logs are in the last file, so start there and loop
1185 // backwards
1186 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1187 it++)
1188 {
1189 std::ifstream logStream(*it);
1190 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001191 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001192 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001193 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001194
Ed Tanous002d39b2022-05-31 08:59:27 -07001195 // Reset the unique ID on the first entry
1196 bool firstEntry = true;
1197 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001198 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001199 std::string idStr;
1200 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001201 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001202 continue;
1203 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001204 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001205
Jason M. Billsde703c52022-06-23 14:19:04 -07001206 nlohmann::json::object_t bmcLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07001207 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
1208 {
1209 messages::internalError(asyncResp->res);
1210 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001211 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001212
1213 if (bmcLogEntry.empty())
1214 {
1215 continue;
1216 }
1217
1218 entryCount++;
1219 // Handle paging using skip (number of entries to skip from the
1220 // start) and top (number of entries to display)
1221 if (entryCount <= delegatedQuery.skip ||
1222 entryCount > delegatedQuery.skip + delegatedQuery.top)
1223 {
1224 continue;
1225 }
1226
1227 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001228 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001229 }
1230 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1231 if (delegatedQuery.skip + delegatedQuery.top < entryCount)
1232 {
1233 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1234 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
1235 std::to_string(delegatedQuery.skip + delegatedQuery.top);
1236 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001237 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001238}
Chicago Duan336e96c2019-07-15 14:22:08 +08001239
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001240inline void requestRoutesJournalEventLogEntry(App& app)
1241{
1242 BMCWEB_ROUTE(
1243 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001244 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001245 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001246 [&app](const crow::Request& req,
1247 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1248 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001249 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001250 {
1251 return;
1252 }
1253 const std::string& targetID = param;
1254
1255 // Go through the log files and check the unique ID for each
1256 // entry to find the target entry
1257 std::vector<std::filesystem::path> redfishLogFiles;
1258 getRedfishLogFiles(redfishLogFiles);
1259 std::string logEntry;
1260
1261 // Oldest logs are in the last file, so start there and loop
1262 // backwards
1263 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1264 it++)
1265 {
1266 std::ifstream logStream(*it);
1267 if (!logStream.is_open())
1268 {
1269 continue;
1270 }
1271
1272 // Reset the unique ID on the first entry
1273 bool firstEntry = true;
1274 while (std::getline(logStream, logEntry))
1275 {
1276 std::string idStr;
1277 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001278 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001279 continue;
1280 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001281 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001282
1283 if (idStr == targetID)
1284 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001285 nlohmann::json::object_t bmcLogEntry;
1286 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) !=
1287 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07001288 {
1289 messages::internalError(asyncResp->res);
1290 return;
1291 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001292 asyncResp->res.jsonValue = std::move(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001293 return;
1294 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001295 }
1296 }
1297 // Requested ID was not found
1298 messages::resourceMissingAtURI(asyncResp->res,
1299 crow::utility::urlFromPieces(targetID));
1300 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001301}
1302
1303inline void requestRoutesDBusEventLogEntryCollection(App& app)
1304{
1305 BMCWEB_ROUTE(app,
1306 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001307 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001308 .methods(boost::beast::http::verb::get)(
1309 [&app](const crow::Request& req,
1310 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001311 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001312 {
1313 return;
1314 }
1315 // Collections don't include the static data added by SubRoute
1316 // because it has a duplicate entry for members
1317 asyncResp->res.jsonValue["@odata.type"] =
1318 "#LogEntryCollection.LogEntryCollection";
1319 asyncResp->res.jsonValue["@odata.id"] =
1320 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1321 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1322 asyncResp->res.jsonValue["Description"] =
1323 "Collection of System Event Log Entries";
1324
1325 // DBus implementation of EventLog/Entries
1326 // Make call to Logging Service to find all log entry objects
1327 crow::connections::systemBus->async_method_call(
1328 [asyncResp](const boost::system::error_code ec,
1329 const dbus::utility::ManagedObjectType& resp) {
1330 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001331 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001332 // TODO Handle for specific error code
1333 BMCWEB_LOG_ERROR
1334 << "getLogEntriesIfaceData resp_handler got error " << ec;
1335 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001336 return;
1337 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001338 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1339 entriesArray = nlohmann::json::array();
1340 for (const auto& objectPath : resp)
1341 {
1342 const uint32_t* id = nullptr;
1343 const uint64_t* timestamp = nullptr;
1344 const uint64_t* updateTimestamp = nullptr;
1345 const std::string* severity = nullptr;
1346 const std::string* message = nullptr;
1347 const std::string* filePath = nullptr;
1348 bool resolved = false;
1349 for (const auto& interfaceMap : objectPath.second)
1350 {
1351 if (interfaceMap.first ==
1352 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001353 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001354 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001355 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001356 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001357 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001358 id = std::get_if<uint32_t>(&propertyMap.second);
1359 }
1360 else if (propertyMap.first == "Timestamp")
1361 {
1362 timestamp =
1363 std::get_if<uint64_t>(&propertyMap.second);
1364 }
1365 else if (propertyMap.first == "UpdateTimestamp")
1366 {
1367 updateTimestamp =
1368 std::get_if<uint64_t>(&propertyMap.second);
1369 }
1370 else if (propertyMap.first == "Severity")
1371 {
1372 severity = std::get_if<std::string>(
1373 &propertyMap.second);
1374 }
1375 else if (propertyMap.first == "Message")
1376 {
1377 message = std::get_if<std::string>(
1378 &propertyMap.second);
1379 }
1380 else if (propertyMap.first == "Resolved")
1381 {
1382 const bool* resolveptr =
1383 std::get_if<bool>(&propertyMap.second);
1384 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001385 {
1386 messages::internalError(asyncResp->res);
1387 return;
1388 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001389 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001390 }
1391 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001392 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001393 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001394 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001395 messages::internalError(asyncResp->res);
1396 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001397 }
1398 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001399 else if (interfaceMap.first ==
1400 "xyz.openbmc_project.Common.FilePath")
1401 {
1402 for (const auto& propertyMap : interfaceMap.second)
1403 {
1404 if (propertyMap.first == "Path")
1405 {
1406 filePath = std::get_if<std::string>(
1407 &propertyMap.second);
1408 }
1409 }
1410 }
1411 }
1412 // Object path without the
1413 // xyz.openbmc_project.Logging.Entry interface, ignore
1414 // and continue.
1415 if (id == nullptr || message == nullptr ||
1416 severity == nullptr || timestamp == nullptr ||
1417 updateTimestamp == nullptr)
1418 {
1419 continue;
1420 }
1421 entriesArray.push_back({});
1422 nlohmann::json& thisEntry = entriesArray.back();
1423 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1424 thisEntry["@odata.id"] =
1425 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1426 std::to_string(*id);
1427 thisEntry["Name"] = "System Event Log Entry";
1428 thisEntry["Id"] = std::to_string(*id);
1429 thisEntry["Message"] = *message;
1430 thisEntry["Resolved"] = resolved;
1431 thisEntry["EntryType"] = "Event";
1432 thisEntry["Severity"] =
1433 translateSeverityDbusToRedfish(*severity);
1434 thisEntry["Created"] =
1435 crow::utility::getDateTimeUintMs(*timestamp);
1436 thisEntry["Modified"] =
1437 crow::utility::getDateTimeUintMs(*updateTimestamp);
1438 if (filePath != nullptr)
1439 {
1440 thisEntry["AdditionalDataURI"] =
1441 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1442 std::to_string(*id) + "/attachment";
1443 }
1444 }
1445 std::sort(
1446 entriesArray.begin(), entriesArray.end(),
1447 [](const nlohmann::json& left, const nlohmann::json& right) {
1448 return (left["Id"] <= right["Id"]);
1449 });
1450 asyncResp->res.jsonValue["Members@odata.count"] =
1451 entriesArray.size();
1452 },
1453 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1454 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001455 });
1456}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001457
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001458inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001459{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001460 BMCWEB_ROUTE(
1461 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001462 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001463 .methods(boost::beast::http::verb::get)(
1464 [&app](const crow::Request& req,
1465 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1466 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001467 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001468 {
1469 return;
1470 }
1471 std::string entryID = param;
1472 dbus::utility::escapePathForDbus(entryID);
1473
1474 // DBus implementation of EventLog/Entries
1475 // Make call to Logging Service to find all log entry objects
1476 crow::connections::systemBus->async_method_call(
1477 [asyncResp, entryID](const boost::system::error_code ec,
1478 const dbus::utility::DBusPropertiesMap& resp) {
1479 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001480 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001481 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1482 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001483 return;
1484 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001485 if (ec)
1486 {
1487 BMCWEB_LOG_ERROR
1488 << "EventLogEntry (DBus) resp_handler got error " << ec;
1489 messages::internalError(asyncResp->res);
1490 return;
1491 }
1492 const uint32_t* id = nullptr;
1493 const uint64_t* timestamp = nullptr;
1494 const uint64_t* updateTimestamp = nullptr;
1495 const std::string* severity = nullptr;
1496 const std::string* message = nullptr;
1497 const std::string* filePath = nullptr;
1498 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001499
Ed Tanous002d39b2022-05-31 08:59:27 -07001500 for (const auto& propertyMap : resp)
1501 {
1502 if (propertyMap.first == "Id")
1503 {
1504 id = std::get_if<uint32_t>(&propertyMap.second);
1505 }
1506 else if (propertyMap.first == "Timestamp")
1507 {
1508 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1509 }
1510 else if (propertyMap.first == "UpdateTimestamp")
1511 {
1512 updateTimestamp =
1513 std::get_if<uint64_t>(&propertyMap.second);
1514 }
1515 else if (propertyMap.first == "Severity")
1516 {
1517 severity = std::get_if<std::string>(&propertyMap.second);
1518 }
1519 else if (propertyMap.first == "Message")
1520 {
1521 message = std::get_if<std::string>(&propertyMap.second);
1522 }
1523 else if (propertyMap.first == "Resolved")
1524 {
1525 const bool* resolveptr =
1526 std::get_if<bool>(&propertyMap.second);
1527 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001528 {
1529 messages::internalError(asyncResp->res);
1530 return;
1531 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001532 resolved = *resolveptr;
1533 }
1534 else if (propertyMap.first == "Path")
1535 {
1536 filePath = std::get_if<std::string>(&propertyMap.second);
1537 }
1538 }
1539 if (id == nullptr || message == nullptr || severity == nullptr ||
1540 timestamp == nullptr || updateTimestamp == nullptr)
1541 {
1542 messages::internalError(asyncResp->res);
1543 return;
1544 }
1545 asyncResp->res.jsonValue["@odata.type"] =
1546 "#LogEntry.v1_8_0.LogEntry";
1547 asyncResp->res.jsonValue["@odata.id"] =
1548 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1549 std::to_string(*id);
1550 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1551 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1552 asyncResp->res.jsonValue["Message"] = *message;
1553 asyncResp->res.jsonValue["Resolved"] = resolved;
1554 asyncResp->res.jsonValue["EntryType"] = "Event";
1555 asyncResp->res.jsonValue["Severity"] =
1556 translateSeverityDbusToRedfish(*severity);
1557 asyncResp->res.jsonValue["Created"] =
1558 crow::utility::getDateTimeUintMs(*timestamp);
1559 asyncResp->res.jsonValue["Modified"] =
1560 crow::utility::getDateTimeUintMs(*updateTimestamp);
1561 if (filePath != nullptr)
1562 {
1563 asyncResp->res.jsonValue["AdditionalDataURI"] =
1564 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1565 std::to_string(*id) + "/attachment";
1566 }
1567 },
1568 "xyz.openbmc_project.Logging",
1569 "/xyz/openbmc_project/logging/entry/" + entryID,
1570 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001571 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001572
1573 BMCWEB_ROUTE(
1574 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001575 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001576 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001577 [&app](const crow::Request& req,
1578 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1579 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001580 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001581 {
1582 return;
1583 }
1584 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001585
Ed Tanous002d39b2022-05-31 08:59:27 -07001586 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1587 resolved))
1588 {
1589 return;
1590 }
1591 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001592
Ed Tanous002d39b2022-05-31 08:59:27 -07001593 crow::connections::systemBus->async_method_call(
1594 [asyncResp, entryId](const boost::system::error_code ec) {
1595 if (ec)
1596 {
1597 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1598 messages::internalError(asyncResp->res);
1599 return;
1600 }
1601 },
1602 "xyz.openbmc_project.Logging",
1603 "/xyz/openbmc_project/logging/entry/" + entryId,
1604 "org.freedesktop.DBus.Properties", "Set",
1605 "xyz.openbmc_project.Logging.Entry", "Resolved",
1606 dbus::utility::DbusVariantType(*resolved));
1607 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001608
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001609 BMCWEB_ROUTE(
1610 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001611 .privileges(redfish::privileges::deleteLogEntry)
1612
Ed Tanous002d39b2022-05-31 08:59:27 -07001613 .methods(boost::beast::http::verb::delete_)(
1614 [&app](const crow::Request& req,
1615 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1616 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001617 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001618 {
1619 return;
1620 }
1621 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1622
1623 std::string entryID = param;
1624
1625 dbus::utility::escapePathForDbus(entryID);
1626
1627 // Process response from Logging service.
1628 auto respHandler =
1629 [asyncResp, entryID](const boost::system::error_code ec) {
1630 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1631 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001632 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001633 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001634 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001635 messages::resourceNotFound(asyncResp->res, "LogEntry",
1636 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001637 return;
1638 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001639 // TODO Handle for specific error code
1640 BMCWEB_LOG_ERROR
1641 << "EventLogEntry (DBus) doDelete respHandler got error "
1642 << ec;
1643 asyncResp->res.result(
1644 boost::beast::http::status::internal_server_error);
1645 return;
1646 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001647
Ed Tanous002d39b2022-05-31 08:59:27 -07001648 asyncResp->res.result(boost::beast::http::status::ok);
1649 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001650
Ed Tanous002d39b2022-05-31 08:59:27 -07001651 // Make call to Logging service to request Delete Log
1652 crow::connections::systemBus->async_method_call(
1653 respHandler, "xyz.openbmc_project.Logging",
1654 "/xyz/openbmc_project/logging/entry/" + entryID,
1655 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001656 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001657}
1658
1659inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001660{
George Liu0fda0f12021-11-16 10:06:17 +08001661 BMCWEB_ROUTE(
1662 app,
1663 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001664 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001665 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001666 [&app](const crow::Request& req,
1667 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1668 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001669 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001670 {
1671 return;
1672 }
1673 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1674 {
1675 asyncResp->res.result(boost::beast::http::status::bad_request);
1676 return;
1677 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001678
Ed Tanous002d39b2022-05-31 08:59:27 -07001679 std::string entryID = param;
1680 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001681
Ed Tanous002d39b2022-05-31 08:59:27 -07001682 crow::connections::systemBus->async_method_call(
1683 [asyncResp, entryID](const boost::system::error_code ec,
1684 const sdbusplus::message::unix_fd& unixfd) {
1685 if (ec.value() == EBADR)
1686 {
1687 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1688 entryID);
1689 return;
1690 }
1691 if (ec)
1692 {
1693 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1694 messages::internalError(asyncResp->res);
1695 return;
1696 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001697
Ed Tanous002d39b2022-05-31 08:59:27 -07001698 int fd = -1;
1699 fd = dup(unixfd);
1700 if (fd == -1)
1701 {
1702 messages::internalError(asyncResp->res);
1703 return;
1704 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001705
Ed Tanous002d39b2022-05-31 08:59:27 -07001706 long long int size = lseek(fd, 0, SEEK_END);
1707 if (size == -1)
1708 {
1709 messages::internalError(asyncResp->res);
1710 return;
1711 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001712
Ed Tanous002d39b2022-05-31 08:59:27 -07001713 // Arbitrary max size of 64kb
1714 constexpr int maxFileSize = 65536;
1715 if (size > maxFileSize)
1716 {
1717 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1718 << maxFileSize;
1719 messages::internalError(asyncResp->res);
1720 return;
1721 }
1722 std::vector<char> data(static_cast<size_t>(size));
1723 long long int rc = lseek(fd, 0, SEEK_SET);
1724 if (rc == -1)
1725 {
1726 messages::internalError(asyncResp->res);
1727 return;
1728 }
1729 rc = read(fd, data.data(), data.size());
1730 if ((rc == -1) || (rc != size))
1731 {
1732 messages::internalError(asyncResp->res);
1733 return;
1734 }
1735 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001736
Ed Tanous002d39b2022-05-31 08:59:27 -07001737 std::string_view strData(data.data(), data.size());
1738 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001739
Ed Tanous002d39b2022-05-31 08:59:27 -07001740 asyncResp->res.addHeader("Content-Type",
1741 "application/octet-stream");
1742 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
1743 asyncResp->res.body() = std::move(output);
1744 },
1745 "xyz.openbmc_project.Logging",
1746 "/xyz/openbmc_project/logging/entry/" + entryID,
1747 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1748 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001749}
1750
Spencer Kub7028eb2021-10-26 15:27:35 +08001751constexpr const char* hostLoggerFolderPath = "/var/log/console";
1752
1753inline bool
1754 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1755 std::vector<std::filesystem::path>& hostLoggerFiles)
1756{
1757 std::error_code ec;
1758 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1759 if (ec)
1760 {
1761 BMCWEB_LOG_ERROR << ec.message();
1762 return false;
1763 }
1764 for (const std::filesystem::directory_entry& it : logPath)
1765 {
1766 std::string filename = it.path().filename();
1767 // Prefix of each log files is "log". Find the file and save the
1768 // path
1769 if (boost::starts_with(filename, "log"))
1770 {
1771 hostLoggerFiles.emplace_back(it.path());
1772 }
1773 }
1774 // As the log files rotate, they are appended with a ".#" that is higher for
1775 // the older logs. Since we start from oldest logs, sort the name in
1776 // descending order.
1777 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1778 AlphanumLess<std::string>());
1779
1780 return true;
1781}
1782
1783inline bool
1784 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
Ed Tanousc937d2b2022-04-05 09:58:00 -07001785 uint64_t skip, uint64_t top,
Spencer Kub7028eb2021-10-26 15:27:35 +08001786 std::vector<std::string>& logEntries, size_t& logCount)
1787{
1788 GzFileReader logFile;
1789
1790 // Go though all log files and expose host logs.
1791 for (const std::filesystem::path& it : hostLoggerFiles)
1792 {
1793 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1794 {
1795 BMCWEB_LOG_ERROR << "fail to expose host logs";
1796 return false;
1797 }
1798 }
1799 // Get lastMessage from constructor by getter
1800 std::string lastMessage = logFile.getLastMessage();
1801 if (!lastMessage.empty())
1802 {
1803 logCount++;
1804 if (logCount > skip && logCount <= (skip + top))
1805 {
1806 logEntries.push_back(lastMessage);
1807 }
1808 }
1809 return true;
1810}
1811
1812inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1813 const std::string& msg,
1814 nlohmann::json& logEntryJson)
1815{
1816 // Fill in the log entry with the gathered data.
1817 logEntryJson = {
1818 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1819 {"@odata.id",
1820 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1821 logEntryID},
1822 {"Name", "Host Logger Entry"},
1823 {"Id", logEntryID},
1824 {"Message", msg},
1825 {"EntryType", "Oem"},
1826 {"Severity", "OK"},
1827 {"OemRecordFormat", "Host Logger Entry"}};
1828}
1829
1830inline void requestRoutesSystemHostLogger(App& app)
1831{
1832 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1833 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001834 .methods(boost::beast::http::verb::get)(
1835 [&app](const crow::Request& req,
1836 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001837 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001838 {
1839 return;
1840 }
1841 asyncResp->res.jsonValue["@odata.id"] =
1842 "/redfish/v1/Systems/system/LogServices/HostLogger";
1843 asyncResp->res.jsonValue["@odata.type"] =
1844 "#LogService.v1_1_0.LogService";
1845 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1846 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1847 asyncResp->res.jsonValue["Id"] = "HostLogger";
1848 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1849 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1850 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001851}
1852
1853inline void requestRoutesSystemHostLoggerCollection(App& app)
1854{
1855 BMCWEB_ROUTE(app,
1856 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1857 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001858 .methods(boost::beast::http::verb::get)(
1859 [&app](const crow::Request& req,
1860 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1861 query_param::QueryCapabilities capabilities = {
1862 .canDelegateTop = true,
1863 .canDelegateSkip = true,
1864 };
1865 query_param::Query delegatedQuery;
1866 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001867 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001868 {
1869 return;
1870 }
1871 asyncResp->res.jsonValue["@odata.id"] =
1872 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1873 asyncResp->res.jsonValue["@odata.type"] =
1874 "#LogEntryCollection.LogEntryCollection";
1875 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1876 asyncResp->res.jsonValue["Description"] =
1877 "Collection of HostLogger Entries";
1878 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1879 logEntryArray = nlohmann::json::array();
1880 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001881
Ed Tanous002d39b2022-05-31 08:59:27 -07001882 std::vector<std::filesystem::path> hostLoggerFiles;
1883 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1884 {
1885 BMCWEB_LOG_ERROR << "fail to get host log file path";
1886 return;
1887 }
1888
1889 size_t logCount = 0;
1890 // This vector only store the entries we want to expose that
1891 // control by skip and top.
1892 std::vector<std::string> logEntries;
1893 if (!getHostLoggerEntries(hostLoggerFiles, delegatedQuery.skip,
1894 delegatedQuery.top, logEntries, logCount))
1895 {
1896 messages::internalError(asyncResp->res);
1897 return;
1898 }
1899 // If vector is empty, that means skip value larger than total
1900 // log count
1901 if (logEntries.empty())
1902 {
1903 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1904 return;
1905 }
1906 if (!logEntries.empty())
1907 {
1908 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001909 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001910 logEntryArray.push_back({});
1911 nlohmann::json& hostLogEntry = logEntryArray.back();
1912 fillHostLoggerEntryJson(std::to_string(delegatedQuery.skip + i),
1913 logEntries[i], hostLogEntry);
George Liu0fda0f12021-11-16 10:06:17 +08001914 }
1915
Ed Tanous002d39b2022-05-31 08:59:27 -07001916 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1917 if (delegatedQuery.skip + delegatedQuery.top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001918 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001919 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1920 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
1921 std::to_string(delegatedQuery.skip + delegatedQuery.top);
George Liu0fda0f12021-11-16 10:06:17 +08001922 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001923 }
George Liu0fda0f12021-11-16 10:06:17 +08001924 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001925}
1926
1927inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1928{
1929 BMCWEB_ROUTE(
1930 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1931 .privileges(redfish::privileges::getLogEntry)
1932 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001933 [&app](const crow::Request& req,
1934 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1935 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001936 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001937 {
1938 return;
1939 }
1940 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001941
Ed Tanous002d39b2022-05-31 08:59:27 -07001942 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001943
Ed Tanous002d39b2022-05-31 08:59:27 -07001944 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1945 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001946
Ed Tanous002d39b2022-05-31 08:59:27 -07001947 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1948 if (ec == std::errc::invalid_argument)
1949 {
1950 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1951 return;
1952 }
1953 if (ec == std::errc::result_out_of_range)
1954 {
1955 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1956 return;
1957 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001958
Ed Tanous002d39b2022-05-31 08:59:27 -07001959 std::vector<std::filesystem::path> hostLoggerFiles;
1960 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1961 {
1962 BMCWEB_LOG_ERROR << "fail to get host log file path";
1963 return;
1964 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001965
Ed Tanous002d39b2022-05-31 08:59:27 -07001966 size_t logCount = 0;
1967 uint64_t top = 1;
1968 std::vector<std::string> logEntries;
1969 // We can get specific entry by skip and top. For example, if we
1970 // want to get nth entry, we can set skip = n-1 and top = 1 to
1971 // get that entry
1972 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1973 logCount))
1974 {
1975 messages::internalError(asyncResp->res);
1976 return;
1977 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001978
Ed Tanous002d39b2022-05-31 08:59:27 -07001979 if (!logEntries.empty())
1980 {
1981 fillHostLoggerEntryJson(targetID, logEntries[0],
1982 asyncResp->res.jsonValue);
1983 return;
1984 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001985
Ed Tanous002d39b2022-05-31 08:59:27 -07001986 // Requested ID was not found
1987 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1988 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001989}
1990
Claire Weinanfdd26902022-03-01 14:18:25 -08001991constexpr char const* dumpManagerIface =
1992 "xyz.openbmc_project.Collection.DeleteAll";
1993inline void handleLogServicesCollectionGet(
1994 crow::App& app, const crow::Request& req,
1995 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1996{
1997 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1998 {
1999 return;
2000 }
2001 // Collections don't include the static data added by SubRoute
2002 // because it has a duplicate entry for members
2003 asyncResp->res.jsonValue["@odata.type"] =
2004 "#LogServiceCollection.LogServiceCollection";
2005 asyncResp->res.jsonValue["@odata.id"] =
2006 "/redfish/v1/Managers/bmc/LogServices";
2007 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2008 asyncResp->res.jsonValue["Description"] =
2009 "Collection of LogServices for this Manager";
2010 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2011 logServiceArray = nlohmann::json::array();
2012
2013#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
2014 logServiceArray.push_back(
2015 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
2016#endif
2017
2018 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2019
2020#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2021 auto respHandler =
2022 [asyncResp](
2023 const boost::system::error_code ec,
2024 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2025 if (ec)
2026 {
2027 BMCWEB_LOG_ERROR
2028 << "handleLogServicesCollectionGet respHandler got error "
2029 << ec;
2030 // Assume that getting an error simply means there are no dump
2031 // LogServices. Return without adding any error response.
2032 return;
2033 }
2034
2035 nlohmann::json& logServiceArrayLocal =
2036 asyncResp->res.jsonValue["Members"];
2037
2038 for (const std::string& path : subTreePaths)
2039 {
2040 if (path == "/xyz/openbmc_project/dump/bmc")
2041 {
2042 logServiceArrayLocal.push_back(
2043 {{"@odata.id",
2044 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
2045 }
2046 else if (path == "/xyz/openbmc_project/dump/faultlog")
2047 {
2048 logServiceArrayLocal.push_back(
2049 {{"@odata.id",
2050 "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
2051 }
2052 }
2053
2054 asyncResp->res.jsonValue["Members@odata.count"] =
2055 logServiceArrayLocal.size();
2056 };
2057
2058 crow::connections::systemBus->async_method_call(
2059 respHandler, "xyz.openbmc_project.ObjectMapper",
2060 "/xyz/openbmc_project/object_mapper",
2061 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2062 "/xyz/openbmc_project/dump", 0,
2063 std::array<const char*, 1>{dumpManagerIface});
2064#endif
2065}
2066
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002067inline void requestRoutesBMCLogServiceCollection(App& app)
2068{
2069 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002070 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002071 .methods(boost::beast::http::verb::get)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002072 std::bind_front(handleLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002073}
Ed Tanous1da66f72018-07-27 16:13:37 -07002074
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002075inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002076{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002077 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002078 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002079 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002080 [&app](const crow::Request& req,
2081 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002082 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002083 {
2084 return;
2085 }
2086 asyncResp->res.jsonValue["@odata.type"] =
2087 "#LogService.v1_1_0.LogService";
2088 asyncResp->res.jsonValue["@odata.id"] =
2089 "/redfish/v1/Managers/bmc/LogServices/Journal";
2090 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2091 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2092 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2093 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302094
Ed Tanous002d39b2022-05-31 08:59:27 -07002095 std::pair<std::string, std::string> redfishDateTimeOffset =
2096 crow::utility::getDateTimeOffsetNow();
2097 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2098 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2099 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302100
Ed Tanous002d39b2022-05-31 08:59:27 -07002101 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2102 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2103 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002104}
Jason M. Billse1f26342018-07-18 12:12:00 -07002105
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002106static int
2107 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2108 sd_journal* journal,
2109 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002110{
2111 // Get the Log Entry contents
2112 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002113
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002114 std::string message;
2115 std::string_view syslogID;
2116 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2117 if (ret < 0)
2118 {
2119 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2120 << strerror(-ret);
2121 }
2122 if (!syslogID.empty())
2123 {
2124 message += std::string(syslogID) + ": ";
2125 }
2126
Ed Tanous39e77502019-03-04 17:35:53 -08002127 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002128 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002129 if (ret < 0)
2130 {
2131 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2132 return 1;
2133 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002134 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002135
2136 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002137 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002138 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002139 if (ret < 0)
2140 {
2141 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002142 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002143
2144 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002145 std::string entryTimeStr;
2146 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002147 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002148 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002149 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002150
2151 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002152 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002153 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002154 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2155 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002156 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002157 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002158 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002159 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002160 {"Severity", severity <= 2 ? "Critical"
2161 : severity <= 4 ? "Warning"
2162 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002163 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002164 {"Created", std::move(entryTimeStr)}};
2165 return 0;
2166}
2167
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002168inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002169{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002170 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002171 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002172 .methods(boost::beast::http::verb::get)(
2173 [&app](const crow::Request& req,
2174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2175 query_param::QueryCapabilities capabilities = {
2176 .canDelegateTop = true,
2177 .canDelegateSkip = true,
2178 };
2179 query_param::Query delegatedQuery;
2180 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002181 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002182 {
2183 return;
2184 }
2185 // Collections don't include the static data added by SubRoute
2186 // because it has a duplicate entry for members
2187 asyncResp->res.jsonValue["@odata.type"] =
2188 "#LogEntryCollection.LogEntryCollection";
2189 asyncResp->res.jsonValue["@odata.id"] =
2190 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2191 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2192 asyncResp->res.jsonValue["Description"] =
2193 "Collection of BMC Journal Entries";
2194 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2195 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002196
Ed Tanous002d39b2022-05-31 08:59:27 -07002197 // Go through the journal and use the timestamp to create a
2198 // unique ID for each entry
2199 sd_journal* journalTmp = nullptr;
2200 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2201 if (ret < 0)
2202 {
2203 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2204 messages::internalError(asyncResp->res);
2205 return;
2206 }
2207 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2208 journalTmp, sd_journal_close);
2209 journalTmp = nullptr;
2210 uint64_t entryCount = 0;
2211 // Reset the unique ID on the first entry
2212 bool firstEntry = true;
2213 SD_JOURNAL_FOREACH(journal.get())
2214 {
2215 entryCount++;
2216 // Handle paging using skip (number of entries to skip from
2217 // the start) and top (number of entries to display)
2218 if (entryCount <= delegatedQuery.skip ||
2219 entryCount > delegatedQuery.skip + delegatedQuery.top)
George Liu0fda0f12021-11-16 10:06:17 +08002220 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002221 continue;
2222 }
2223
2224 std::string idStr;
2225 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2226 {
2227 continue;
2228 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002229 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002230
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002231 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002232 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2233 bmcJournalLogEntry) != 0)
2234 {
George Liu0fda0f12021-11-16 10:06:17 +08002235 messages::internalError(asyncResp->res);
2236 return;
2237 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002238 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002239 }
2240 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2241 if (delegatedQuery.skip + delegatedQuery.top < entryCount)
2242 {
2243 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2244 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2245 std::to_string(delegatedQuery.skip + delegatedQuery.top);
2246 }
George Liu0fda0f12021-11-16 10:06:17 +08002247 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002248}
Jason M. Billse1f26342018-07-18 12:12:00 -07002249
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002250inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002251{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002252 BMCWEB_ROUTE(app,
2253 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002254 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002255 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002256 [&app](const crow::Request& req,
2257 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2258 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002259 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002260 {
2261 return;
2262 }
2263 // Convert the unique ID back to a timestamp to find the entry
2264 uint64_t ts = 0;
2265 uint64_t index = 0;
2266 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2267 {
2268 return;
2269 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002270
Ed Tanous002d39b2022-05-31 08:59:27 -07002271 sd_journal* journalTmp = nullptr;
2272 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2273 if (ret < 0)
2274 {
2275 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2276 messages::internalError(asyncResp->res);
2277 return;
2278 }
2279 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2280 journalTmp, sd_journal_close);
2281 journalTmp = nullptr;
2282 // Go to the timestamp in the log and move to the entry at the
2283 // index tracking the unique ID
2284 std::string idStr;
2285 bool firstEntry = true;
2286 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2287 if (ret < 0)
2288 {
2289 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2290 << strerror(-ret);
2291 messages::internalError(asyncResp->res);
2292 return;
2293 }
2294 for (uint64_t i = 0; i <= index; i++)
2295 {
2296 sd_journal_next(journal.get());
2297 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2298 {
2299 messages::internalError(asyncResp->res);
2300 return;
2301 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002302 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002303 }
2304 // Confirm that the entry ID matches what was requested
2305 if (idStr != entryID)
2306 {
2307 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2308 return;
2309 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002310
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002311 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002312 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002313 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002314 {
2315 messages::internalError(asyncResp->res);
2316 return;
2317 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002318 asyncResp->res.jsonValue = std::move(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002319 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002320}
2321
Claire Weinanfdd26902022-03-01 14:18:25 -08002322inline void
2323 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2324 const std::string& dumpType)
2325{
2326 std::string dumpPath;
2327 std::string overWritePolicy;
2328 bool collectDiagnosticDataSupported = false;
2329
2330 if (dumpType == "BMC")
2331 {
2332 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2333 overWritePolicy = "WrapsWhenFull";
2334 collectDiagnosticDataSupported = true;
2335 }
2336 else if (dumpType == "FaultLog")
2337 {
2338 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2339 overWritePolicy = "Unknown";
2340 collectDiagnosticDataSupported = false;
2341 }
2342 else if (dumpType == "System")
2343 {
2344 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2345 overWritePolicy = "WrapsWhenFull";
2346 collectDiagnosticDataSupported = true;
2347 }
2348 else
2349 {
2350 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2351 << dumpType;
2352 messages::internalError(asyncResp->res);
2353 return;
2354 }
2355
2356 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2357 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2358 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2359 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2360 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2361 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2362
2363 std::pair<std::string, std::string> redfishDateTimeOffset =
2364 crow::utility::getDateTimeOffsetNow();
2365 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2366 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2367 redfishDateTimeOffset.second;
2368
2369 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2370 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2371 dumpPath + "/Actions/LogService.ClearLog";
2372
2373 if (collectDiagnosticDataSupported)
2374 {
2375 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2376 ["target"] =
2377 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2378 }
2379}
2380
2381inline void handleLogServicesDumpServiceGet(
2382 crow::App& app, const std::string& dumpType, const crow::Request& req,
2383 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2384{
2385 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2386 {
2387 return;
2388 }
2389 getDumpServiceInfo(asyncResp, dumpType);
2390}
2391
2392inline void handleLogServicesDumpEntriesCollectionGet(
2393 crow::App& app, const std::string& dumpType, const crow::Request& req,
2394 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2395{
2396 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2397 {
2398 return;
2399 }
2400 getDumpEntryCollection(asyncResp, dumpType);
2401}
2402
2403inline void handleLogServicesDumpEntryGet(
2404 crow::App& app, const std::string& dumpType, const crow::Request& req,
2405 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2406 const std::string& dumpId)
2407{
2408 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2409 {
2410 return;
2411 }
2412 getDumpEntryById(asyncResp, dumpId, dumpType);
2413}
2414
2415inline void handleLogServicesDumpEntryDelete(
2416 crow::App& app, const std::string& dumpType, const crow::Request& req,
2417 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2418 const std::string& dumpId)
2419{
2420 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2421 {
2422 return;
2423 }
2424 deleteDumpEntry(asyncResp, dumpId, dumpType);
2425}
2426
2427inline void handleLogServicesDumpCollectDiagnosticDataPost(
2428 crow::App& app, const std::string& dumpType, const crow::Request& req,
2429 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2430{
2431 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2432 {
2433 return;
2434 }
2435 createDump(asyncResp, req, dumpType);
2436}
2437
2438inline void handleLogServicesDumpClearLogPost(
2439 crow::App& app, const std::string& dumpType, const crow::Request& req,
2440 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2441{
2442 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2443 {
2444 return;
2445 }
2446 clearDump(asyncResp, dumpType);
2447}
2448
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002449inline void requestRoutesBMCDumpService(App& app)
2450{
2451 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002452 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002453 .methods(boost::beast::http::verb::get)(std::bind_front(
2454 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002455}
2456
2457inline void requestRoutesBMCDumpEntryCollection(App& app)
2458{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002459 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002460 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002461 .methods(boost::beast::http::verb::get)(std::bind_front(
2462 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002463}
2464
2465inline void requestRoutesBMCDumpEntry(App& app)
2466{
2467 BMCWEB_ROUTE(app,
2468 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002469 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002470 .methods(boost::beast::http::verb::get)(std::bind_front(
2471 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2472
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002473 BMCWEB_ROUTE(app,
2474 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002475 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002476 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2477 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002478}
2479
2480inline void requestRoutesBMCDumpCreate(App& app)
2481{
George Liu0fda0f12021-11-16 10:06:17 +08002482 BMCWEB_ROUTE(
2483 app,
2484 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002485 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002486 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002487 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2488 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002489}
2490
2491inline void requestRoutesBMCDumpClear(App& app)
2492{
George Liu0fda0f12021-11-16 10:06:17 +08002493 BMCWEB_ROUTE(
2494 app,
2495 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002496 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002497 .methods(boost::beast::http::verb::post)(std::bind_front(
2498 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2499}
2500
2501inline void requestRoutesFaultLogDumpService(App& app)
2502{
2503 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2504 .privileges(redfish::privileges::getLogService)
2505 .methods(boost::beast::http::verb::get)(std::bind_front(
2506 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2507}
2508
2509inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2510{
2511 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2512 .privileges(redfish::privileges::getLogEntryCollection)
2513 .methods(boost::beast::http::verb::get)(
2514 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2515 std::ref(app), "FaultLog"));
2516}
2517
2518inline void requestRoutesFaultLogDumpEntry(App& app)
2519{
2520 BMCWEB_ROUTE(app,
2521 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2522 .privileges(redfish::privileges::getLogEntry)
2523 .methods(boost::beast::http::verb::get)(std::bind_front(
2524 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2525
2526 BMCWEB_ROUTE(app,
2527 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2528 .privileges(redfish::privileges::deleteLogEntry)
2529 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2530 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2531}
2532
2533inline void requestRoutesFaultLogDumpClear(App& app)
2534{
2535 BMCWEB_ROUTE(
2536 app,
2537 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2538 .privileges(redfish::privileges::postLogService)
2539 .methods(boost::beast::http::verb::post)(std::bind_front(
2540 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002541}
2542
2543inline void requestRoutesSystemDumpService(App& app)
2544{
2545 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002546 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002547 .methods(boost::beast::http::verb::get)(
2548 [&app](const crow::Request& req,
2549 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002550 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002551 {
2552 return;
2553 }
2554 asyncResp->res.jsonValue["@odata.id"] =
2555 "/redfish/v1/Systems/system/LogServices/Dump";
2556 asyncResp->res.jsonValue["@odata.type"] =
2557 "#LogService.v1_2_0.LogService";
2558 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2559 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2560 asyncResp->res.jsonValue["Id"] = "Dump";
2561 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302562
Ed Tanous002d39b2022-05-31 08:59:27 -07002563 std::pair<std::string, std::string> redfishDateTimeOffset =
2564 crow::utility::getDateTimeOffsetNow();
2565 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2566 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2567 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302568
Ed Tanous002d39b2022-05-31 08:59:27 -07002569 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2570 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2571 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2572 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002573
Ed Tanous002d39b2022-05-31 08:59:27 -07002574 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2575 ["target"] =
2576 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002577 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002578}
2579
2580inline void requestRoutesSystemDumpEntryCollection(App& app)
2581{
2582
2583 /**
2584 * Functions triggers appropriate requests on DBus
2585 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002586 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002587 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002588 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002589 [&app](const crow::Request& req,
2590 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002591 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002592 {
2593 return;
2594 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002595 getDumpEntryCollection(asyncResp, "System");
2596 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002597}
2598
2599inline void requestRoutesSystemDumpEntry(App& app)
2600{
2601 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002602 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002603 .privileges(redfish::privileges::getLogEntry)
2604
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002605 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002606 [&app](const crow::Request& req,
2607 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2608 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002609 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002610 {
2611 return;
2612 }
2613 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002614 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002615
2616 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002617 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002618 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002619 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002620 [&app](const crow::Request& req,
2621 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2622 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002623 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002624 {
2625 return;
2626 }
2627 deleteDumpEntry(asyncResp, param, "system");
2628 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002629}
2630
2631inline void requestRoutesSystemDumpCreate(App& app)
2632{
George Liu0fda0f12021-11-16 10:06:17 +08002633 BMCWEB_ROUTE(
2634 app,
2635 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002636 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002637 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002638 [&app](const crow::Request& req,
2639 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002640 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002641 {
2642 return;
2643 }
2644 createDump(asyncResp, req, "System");
2645 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002646}
2647
2648inline void requestRoutesSystemDumpClear(App& app)
2649{
George Liu0fda0f12021-11-16 10:06:17 +08002650 BMCWEB_ROUTE(
2651 app,
2652 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002653 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002654 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002655 [&app](const crow::Request& req,
2656 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002657
Ed Tanous45ca1b82022-03-25 13:07:27 -07002658 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002659 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002660 {
2661 return;
2662 }
2663 clearDump(asyncResp, "System");
2664 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002665}
2666
2667inline void requestRoutesCrashdumpService(App& app)
2668{
2669 // Note: Deviated from redfish privilege registry for GET & HEAD
2670 // method for security reasons.
2671 /**
2672 * Functions triggers appropriate requests on DBus
2673 */
2674 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002675 // This is incorrect, should be:
2676 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002677 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002678 .methods(boost::beast::http::verb::get)(
2679 [&app](const crow::Request& req,
2680 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002681 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002682 {
2683 return;
2684 }
2685 // Copy over the static data to include the entries added by
2686 // SubRoute
2687 asyncResp->res.jsonValue["@odata.id"] =
2688 "/redfish/v1/Systems/system/LogServices/Crashdump";
2689 asyncResp->res.jsonValue["@odata.type"] =
2690 "#LogService.v1_2_0.LogService";
2691 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2692 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2693 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2694 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2695 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302696
Ed Tanous002d39b2022-05-31 08:59:27 -07002697 std::pair<std::string, std::string> redfishDateTimeOffset =
2698 crow::utility::getDateTimeOffsetNow();
2699 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2700 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2701 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302702
Ed Tanous002d39b2022-05-31 08:59:27 -07002703 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2704 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2705 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2706 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2707 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2708 ["target"] =
2709 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002710 });
2711}
2712
2713void inline requestRoutesCrashdumpClear(App& app)
2714{
George Liu0fda0f12021-11-16 10:06:17 +08002715 BMCWEB_ROUTE(
2716 app,
2717 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002718 // This is incorrect, should be:
2719 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002720 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002721 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002722 [&app](const crow::Request& req,
2723 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002724 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002725 {
2726 return;
2727 }
2728 crow::connections::systemBus->async_method_call(
2729 [asyncResp](const boost::system::error_code ec,
2730 const std::string&) {
2731 if (ec)
2732 {
2733 messages::internalError(asyncResp->res);
2734 return;
2735 }
2736 messages::success(asyncResp->res);
2737 },
2738 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2739 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002740}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002741
zhanghch058d1b46d2021-04-01 11:18:24 +08002742static void
2743 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2744 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002745{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002746 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002747 [asyncResp, logID,
2748 &logEntryJson](const boost::system::error_code ec,
2749 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002750 if (ec)
2751 {
2752 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2753 if (ec.value() ==
2754 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002755 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002756 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002757 }
2758 else
2759 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002760 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002761 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002762 return;
2763 }
2764
2765 std::string timestamp{};
2766 std::string filename{};
2767 std::string logfile{};
2768 parseCrashdumpParameters(params, filename, timestamp, logfile);
2769
2770 if (filename.empty() || timestamp.empty())
2771 {
2772 messages::resourceMissingAtURI(asyncResp->res,
2773 crow::utility::urlFromPieces(logID));
2774 return;
2775 }
2776
2777 std::string crashdumpURI =
2778 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2779 logID + "/" + filename;
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002780 nlohmann::json::object_t logEntry = {
Ed Tanous002d39b2022-05-31 08:59:27 -07002781 {"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
2782 {"@odata.id",
2783 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2784 logID},
2785 {"Name", "CPU Crashdump"},
2786 {"Id", logID},
2787 {"EntryType", "Oem"},
2788 {"AdditionalDataURI", std::move(crashdumpURI)},
2789 {"DiagnosticDataType", "OEM"},
2790 {"OEMDiagnosticDataType", "PECICrashdump"},
2791 {"Created", std::move(timestamp)}};
2792
2793 // If logEntryJson references an array of LogEntry resources
2794 // ('Members' list), then push this as a new entry, otherwise set it
2795 // directly
2796 if (logEntryJson.is_array())
2797 {
2798 logEntryJson.push_back(logEntry);
2799 asyncResp->res.jsonValue["Members@odata.count"] =
2800 logEntryJson.size();
2801 }
2802 else
2803 {
2804 logEntryJson = logEntry;
2805 }
2806 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002807 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002808 std::move(getStoredLogCallback), crashdumpObject,
2809 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002810 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002811}
2812
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002813inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002814{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002815 // Note: Deviated from redfish privilege registry for GET & HEAD
2816 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002817 /**
2818 * Functions triggers appropriate requests on DBus
2819 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002820 BMCWEB_ROUTE(app,
2821 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002822 // This is incorrect, should be.
2823 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002824 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002825 .methods(boost::beast::http::verb::get)(
2826 [&app](const crow::Request& req,
2827 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002828 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002829 {
2830 return;
2831 }
2832 crow::connections::systemBus->async_method_call(
2833 [asyncResp](const boost::system::error_code ec,
2834 const std::vector<std::string>& resp) {
2835 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002836 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002837 if (ec.value() !=
2838 boost::system::errc::no_such_file_or_directory)
2839 {
2840 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2841 << ec.message();
2842 messages::internalError(asyncResp->res);
2843 return;
2844 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002845 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002846 asyncResp->res.jsonValue["@odata.type"] =
2847 "#LogEntryCollection.LogEntryCollection";
2848 asyncResp->res.jsonValue["@odata.id"] =
2849 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2850 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2851 asyncResp->res.jsonValue["Description"] =
2852 "Collection of Crashdump Entries";
2853 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2854 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002855
Ed Tanous002d39b2022-05-31 08:59:27 -07002856 for (const std::string& path : resp)
2857 {
2858 const sdbusplus::message::object_path objPath(path);
2859 // Get the log ID
2860 std::string logID = objPath.filename();
2861 if (logID.empty())
2862 {
2863 continue;
2864 }
2865 // Add the log entry to the array
2866 logCrashdumpEntry(asyncResp, logID,
2867 asyncResp->res.jsonValue["Members"]);
2868 }
2869 },
2870 "xyz.openbmc_project.ObjectMapper",
2871 "/xyz/openbmc_project/object_mapper",
2872 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2873 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002874 });
2875}
Ed Tanous1da66f72018-07-27 16:13:37 -07002876
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002877inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002878{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002879 // Note: Deviated from redfish privilege registry for GET & HEAD
2880 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002881
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002882 BMCWEB_ROUTE(
2883 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002884 // this is incorrect, should be
2885 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002886 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002887 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002888 [&app](const crow::Request& req,
2889 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2890 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002891 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002892 {
2893 return;
2894 }
2895 const std::string& logID = param;
2896 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2897 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002898}
Ed Tanous1da66f72018-07-27 16:13:37 -07002899
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002900inline void requestRoutesCrashdumpFile(App& app)
2901{
2902 // Note: Deviated from redfish privilege registry for GET & HEAD
2903 // method for security reasons.
2904 BMCWEB_ROUTE(
2905 app,
2906 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002907 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002908 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002909 [&app](const crow::Request& req,
2910 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2911 const std::string& logID, const std::string& fileName) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002912 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002913 {
2914 return;
2915 }
2916 auto getStoredLogCallback =
2917 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2918 const boost::system::error_code ec,
2919 const std::vector<
2920 std::pair<std::string, dbus::utility::DbusVariantType>>&
2921 resp) {
2922 if (ec)
2923 {
2924 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2925 messages::internalError(asyncResp->res);
2926 return;
2927 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002928
Ed Tanous002d39b2022-05-31 08:59:27 -07002929 std::string dbusFilename{};
2930 std::string dbusTimestamp{};
2931 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002932
Ed Tanous002d39b2022-05-31 08:59:27 -07002933 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2934 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002935
Ed Tanous002d39b2022-05-31 08:59:27 -07002936 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2937 dbusFilepath.empty())
2938 {
2939 messages::resourceMissingAtURI(asyncResp->res, url);
2940 return;
2941 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002942
Ed Tanous002d39b2022-05-31 08:59:27 -07002943 // Verify the file name parameter is correct
2944 if (fileName != dbusFilename)
2945 {
2946 messages::resourceMissingAtURI(asyncResp->res, url);
2947 return;
2948 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002949
Ed Tanous002d39b2022-05-31 08:59:27 -07002950 if (!std::filesystem::exists(dbusFilepath))
2951 {
2952 messages::resourceMissingAtURI(asyncResp->res, url);
2953 return;
2954 }
2955 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2956 asyncResp->res.body() =
2957 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002958
Ed Tanous002d39b2022-05-31 08:59:27 -07002959 // Configure this to be a file download when accessed
2960 // from a browser
2961 asyncResp->res.addHeader("Content-Disposition", "attachment");
2962 };
2963 crow::connections::systemBus->async_method_call(
2964 std::move(getStoredLogCallback), crashdumpObject,
2965 crashdumpPath + std::string("/") + logID,
2966 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2967 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002968}
2969
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002970enum class OEMDiagnosticType
2971{
2972 onDemand,
2973 telemetry,
2974 invalid,
2975};
2976
Ed Tanousf7725d72022-03-07 12:46:00 -08002977inline OEMDiagnosticType
2978 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002979{
2980 if (oemDiagStr == "OnDemand")
2981 {
2982 return OEMDiagnosticType::onDemand;
2983 }
2984 if (oemDiagStr == "Telemetry")
2985 {
2986 return OEMDiagnosticType::telemetry;
2987 }
2988
2989 return OEMDiagnosticType::invalid;
2990}
2991
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002992inline void requestRoutesCrashdumpCollect(App& app)
2993{
2994 // Note: Deviated from redfish privilege registry for GET & HEAD
2995 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002996 BMCWEB_ROUTE(
2997 app,
2998 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002999 // The below is incorrect; Should be ConfigureManager
3000 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003001 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07003002 .methods(boost::beast::http::verb::post)(
3003 [&app](const crow::Request& req,
3004 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003005 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003006 {
3007 return;
3008 }
3009 std::string diagnosticDataType;
3010 std::string oemDiagnosticDataType;
3011 if (!redfish::json_util::readJsonAction(
3012 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3013 "OEMDiagnosticDataType", oemDiagnosticDataType))
3014 {
3015 return;
3016 }
3017
3018 if (diagnosticDataType != "OEM")
3019 {
3020 BMCWEB_LOG_ERROR
3021 << "Only OEM DiagnosticDataType supported for Crashdump";
3022 messages::actionParameterValueFormatError(
3023 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3024 "CollectDiagnosticData");
3025 return;
3026 }
3027
3028 OEMDiagnosticType oemDiagType =
3029 getOEMDiagnosticType(oemDiagnosticDataType);
3030
3031 std::string iface;
3032 std::string method;
3033 std::string taskMatchStr;
3034 if (oemDiagType == OEMDiagnosticType::onDemand)
3035 {
3036 iface = crashdumpOnDemandInterface;
3037 method = "GenerateOnDemandLog";
3038 taskMatchStr = "type='signal',"
3039 "interface='org.freedesktop.DBus.Properties',"
3040 "member='PropertiesChanged',"
3041 "arg0namespace='com.intel.crashdump'";
3042 }
3043 else if (oemDiagType == OEMDiagnosticType::telemetry)
3044 {
3045 iface = crashdumpTelemetryInterface;
3046 method = "GenerateTelemetryLog";
3047 taskMatchStr = "type='signal',"
3048 "interface='org.freedesktop.DBus.Properties',"
3049 "member='PropertiesChanged',"
3050 "arg0namespace='com.intel.crashdump'";
3051 }
3052 else
3053 {
3054 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3055 << oemDiagnosticDataType;
3056 messages::actionParameterValueFormatError(
3057 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3058 "CollectDiagnosticData");
3059 return;
3060 }
3061
3062 auto collectCrashdumpCallback =
3063 [asyncResp, payload(task::Payload(req)),
3064 taskMatchStr](const boost::system::error_code ec,
3065 const std::string&) mutable {
3066 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003067 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003068 if (ec.value() == boost::system::errc::operation_not_supported)
3069 {
3070 messages::resourceInStandby(asyncResp->res);
3071 }
3072 else if (ec.value() ==
3073 boost::system::errc::device_or_resource_busy)
3074 {
3075 messages::serviceTemporarilyUnavailable(asyncResp->res,
3076 "60");
3077 }
3078 else
3079 {
3080 messages::internalError(asyncResp->res);
3081 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003082 return;
3083 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003084 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
3085 [](boost::system::error_code err, sdbusplus::message::message&,
3086 const std::shared_ptr<task::TaskData>& taskData) {
3087 if (!err)
3088 {
3089 taskData->messages.emplace_back(messages::taskCompletedOK(
3090 std::to_string(taskData->index)));
3091 taskData->state = "Completed";
3092 }
3093 return task::completed;
3094 },
3095 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003096
Ed Tanous002d39b2022-05-31 08:59:27 -07003097 task->startTimer(std::chrono::minutes(5));
3098 task->populateResp(asyncResp->res);
3099 task->payload.emplace(std::move(payload));
3100 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003101
Ed Tanous002d39b2022-05-31 08:59:27 -07003102 crow::connections::systemBus->async_method_call(
3103 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3104 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003105 });
3106}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003107
Andrew Geisslercb92c032018-08-17 07:56:14 -07003108/**
3109 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3110 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003111inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003112{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003113 /**
3114 * Function handles POST method request.
3115 * The Clear Log actions does not require any parameter.The action deletes
3116 * all entries found in the Entries collection for this Log Service.
3117 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003118
George Liu0fda0f12021-11-16 10:06:17 +08003119 BMCWEB_ROUTE(
3120 app,
3121 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003122 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003123 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003124 [&app](const crow::Request& req,
3125 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003126 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003127 {
3128 return;
3129 }
3130 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003131
Ed Tanous002d39b2022-05-31 08:59:27 -07003132 // Process response from Logging service.
3133 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3134 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3135 if (ec)
3136 {
3137 // TODO Handle for specific error code
3138 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3139 asyncResp->res.result(
3140 boost::beast::http::status::internal_server_error);
3141 return;
3142 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003143
Ed Tanous002d39b2022-05-31 08:59:27 -07003144 asyncResp->res.result(boost::beast::http::status::no_content);
3145 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003146
Ed Tanous002d39b2022-05-31 08:59:27 -07003147 // Make call to Logging service to request Clear Log
3148 crow::connections::systemBus->async_method_call(
3149 respHandler, "xyz.openbmc_project.Logging",
3150 "/xyz/openbmc_project/logging",
3151 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3152 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003153}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003154
3155/****************************************************
3156 * Redfish PostCode interfaces
3157 * using DBUS interface: getPostCodesTS
3158 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003159inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003160{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003161 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003162 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003163 .methods(boost::beast::http::verb::get)(
3164 [&app](const crow::Request& req,
3165 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003166 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003167 {
3168 return;
3169 }
Ed Tanous14766872022-03-15 10:44:42 -07003170
Ed Tanous002d39b2022-05-31 08:59:27 -07003171 asyncResp->res.jsonValue["@odata.id"] =
3172 "/redfish/v1/Systems/system/LogServices/PostCodes";
3173 asyncResp->res.jsonValue["@odata.type"] =
3174 "#LogService.v1_1_0.LogService";
3175 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3176 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3177 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3178 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3179 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3180 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303181
Ed Tanous002d39b2022-05-31 08:59:27 -07003182 std::pair<std::string, std::string> redfishDateTimeOffset =
3183 crow::utility::getDateTimeOffsetNow();
3184 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3185 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3186 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303187
Ed Tanous002d39b2022-05-31 08:59:27 -07003188 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3189 {"target",
3190 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003191 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003192}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003193
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003194inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003195{
George Liu0fda0f12021-11-16 10:06:17 +08003196 BMCWEB_ROUTE(
3197 app,
3198 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003199 // The following privilege is incorrect; It should be ConfigureManager
3200 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003201 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003202 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003203 [&app](const crow::Request& req,
3204 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003205 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003206 {
3207 return;
3208 }
3209 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003210
Ed Tanous002d39b2022-05-31 08:59:27 -07003211 // Make call to post-code service to request clear all
3212 crow::connections::systemBus->async_method_call(
3213 [asyncResp](const boost::system::error_code ec) {
3214 if (ec)
3215 {
3216 // TODO Handle for specific error code
3217 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3218 << ec;
3219 asyncResp->res.result(
3220 boost::beast::http::status::internal_server_error);
3221 messages::internalError(asyncResp->res);
3222 return;
3223 }
3224 },
3225 "xyz.openbmc_project.State.Boot.PostCode0",
3226 "/xyz/openbmc_project/State/Boot/PostCode0",
3227 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3228 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003229}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003230
3231static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003232 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303233 const boost::container::flat_map<
3234 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003235 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3236 const uint64_t skip = 0, const uint64_t top = 0)
3237{
3238 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003239 const registries::Message* message =
3240 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003241
3242 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003243 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003244
3245 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303246 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3247 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003248 {
3249 currentCodeIndex++;
3250 std::string postcodeEntryID =
3251 "B" + std::to_string(bootIndex) + "-" +
3252 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3253
3254 uint64_t usecSinceEpoch = code.first;
3255 uint64_t usTimeOffset = 0;
3256
3257 if (1 == currentCodeIndex)
3258 { // already incremented
3259 firstCodeTimeUs = code.first;
3260 }
3261 else
3262 {
3263 usTimeOffset = code.first - firstCodeTimeUs;
3264 }
3265
3266 // skip if no specific codeIndex is specified and currentCodeIndex does
3267 // not fall between top and skip
3268 if ((codeIndex == 0) &&
3269 (currentCodeIndex <= skip || currentCodeIndex > top))
3270 {
3271 continue;
3272 }
3273
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003274 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003275 // currentIndex
3276 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3277 {
3278 // This is done for simplicity. 1st entry is needed to calculate
3279 // time offset. To improve efficiency, one can get to the entry
3280 // directly (possibly with flatmap's nth method)
3281 continue;
3282 }
3283
3284 // currentCodeIndex is within top and skip or equal to specified code
3285 // index
3286
3287 // Get the Created time from the timestamp
3288 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003289 entryTimeStr =
3290 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003291
3292 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3293 std::ostringstream hexCode;
3294 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303295 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003296 std::ostringstream timeOffsetStr;
3297 // Set Fixed -Point Notation
3298 timeOffsetStr << std::fixed;
3299 // Set precision to 4 digits
3300 timeOffsetStr << std::setprecision(4);
3301 // Add double to stream
3302 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3303 std::vector<std::string> messageArgs = {
3304 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3305
3306 // Get MessageArgs template from message registry
3307 std::string msg;
3308 if (message != nullptr)
3309 {
3310 msg = message->message;
3311
3312 // fill in this post code value
3313 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003314 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003315 {
3316 std::string argStr = "%" + std::to_string(++i);
3317 size_t argPos = msg.find(argStr);
3318 if (argPos != std::string::npos)
3319 {
3320 msg.replace(argPos, argStr.length(), messageArg);
3321 }
3322 }
3323 }
3324
Tim Leed4342a92020-04-27 11:47:58 +08003325 // Get Severity template from message registry
3326 std::string severity;
3327 if (message != nullptr)
3328 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003329 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003330 }
3331
ZhikuiRena3316fc2020-01-29 14:58:08 -08003332 // add to AsyncResp
3333 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003334 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003335 bmcLogEntry = {
3336 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3337 {"@odata.id",
3338 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3339 postcodeEntryID},
3340 {"Name", "POST Code Log Entry"},
3341 {"Id", postcodeEntryID},
3342 {"Message", std::move(msg)},
3343 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3344 {"MessageArgs", std::move(messageArgs)},
3345 {"EntryType", "Event"},
3346 {"Severity", std::move(severity)},
3347 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003348 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3349 {
3350 bmcLogEntry["AdditionalDataURI"] =
3351 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3352 postcodeEntryID + "/attachment";
3353 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003354 }
3355}
3356
zhanghch058d1b46d2021-04-01 11:18:24 +08003357static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003358 const uint16_t bootIndex,
3359 const uint64_t codeIndex)
3360{
3361 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303362 [aResp, bootIndex,
3363 codeIndex](const boost::system::error_code ec,
3364 const boost::container::flat_map<
3365 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3366 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003367 if (ec)
3368 {
3369 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3370 messages::internalError(aResp->res);
3371 return;
3372 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003373
Ed Tanous002d39b2022-05-31 08:59:27 -07003374 // skip the empty postcode boots
3375 if (postcode.empty())
3376 {
3377 return;
3378 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003379
Ed Tanous002d39b2022-05-31 08:59:27 -07003380 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003381
Ed Tanous002d39b2022-05-31 08:59:27 -07003382 aResp->res.jsonValue["Members@odata.count"] =
3383 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003384 },
Jonathan Doman15124762021-01-07 17:54:17 -08003385 "xyz.openbmc_project.State.Boot.PostCode0",
3386 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003387 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3388 bootIndex);
3389}
3390
zhanghch058d1b46d2021-04-01 11:18:24 +08003391static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003392 const uint16_t bootIndex,
3393 const uint16_t bootCount,
3394 const uint64_t entryCount, const uint64_t skip,
3395 const uint64_t top)
3396{
3397 crow::connections::systemBus->async_method_call(
3398 [aResp, bootIndex, bootCount, entryCount, skip,
3399 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303400 const boost::container::flat_map<
3401 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3402 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003403 if (ec)
3404 {
3405 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3406 messages::internalError(aResp->res);
3407 return;
3408 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003409
Ed Tanous002d39b2022-05-31 08:59:27 -07003410 uint64_t endCount = entryCount;
3411 if (!postcode.empty())
3412 {
3413 endCount = entryCount + postcode.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003414
Ed Tanous002d39b2022-05-31 08:59:27 -07003415 if ((skip < endCount) && ((top + skip) > entryCount))
ZhikuiRena3316fc2020-01-29 14:58:08 -08003416 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003417 uint64_t thisBootSkip = std::max(skip, entryCount) - entryCount;
3418 uint64_t thisBootTop =
3419 std::min(top + skip, endCount) - entryCount;
3420
3421 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3422 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003423 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003424 aResp->res.jsonValue["Members@odata.count"] = endCount;
3425 }
3426
3427 // continue to previous bootIndex
3428 if (bootIndex < bootCount)
3429 {
3430 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3431 bootCount, endCount, skip, top);
3432 }
3433 else
3434 {
3435 aResp->res.jsonValue["Members@odata.nextLink"] =
3436 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3437 std::to_string(skip + top);
3438 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003439 },
Jonathan Doman15124762021-01-07 17:54:17 -08003440 "xyz.openbmc_project.State.Boot.PostCode0",
3441 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003442 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3443 bootIndex);
3444}
3445
zhanghch058d1b46d2021-04-01 11:18:24 +08003446static void
3447 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3448 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003449{
3450 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003451 sdbusplus::asio::getProperty<uint16_t>(
3452 *crow::connections::systemBus,
3453 "xyz.openbmc_project.State.Boot.PostCode0",
3454 "/xyz/openbmc_project/State/Boot/PostCode0",
3455 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3456 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3457 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003458 if (ec)
3459 {
3460 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3461 messages::internalError(aResp->res);
3462 return;
3463 }
3464 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003465 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003466}
3467
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003468inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003469{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003470 BMCWEB_ROUTE(app,
3471 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003472 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003473 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003474 [&app](const crow::Request& req,
3475 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003476 query_param::QueryCapabilities capabilities = {
3477 .canDelegateTop = true,
3478 .canDelegateSkip = true,
3479 };
3480 query_param::Query delegatedQuery;
3481 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003482 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003483 {
3484 return;
3485 }
3486 asyncResp->res.jsonValue["@odata.type"] =
3487 "#LogEntryCollection.LogEntryCollection";
3488 asyncResp->res.jsonValue["@odata.id"] =
3489 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3490 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3491 asyncResp->res.jsonValue["Description"] =
3492 "Collection of POST Code Log Entries";
3493 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3494 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003495
Ed Tanous002d39b2022-05-31 08:59:27 -07003496 getCurrentBootNumber(asyncResp, delegatedQuery.skip,
3497 delegatedQuery.top);
3498 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003499}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003500
George Liu647b3cd2021-07-05 12:43:56 +08003501/**
3502 * @brief Parse post code ID and get the current value and index value
3503 * eg: postCodeID=B1-2, currentValue=1, index=2
3504 *
3505 * @param[in] postCodeID Post Code ID
3506 * @param[out] currentValue Current value
3507 * @param[out] index Index value
3508 *
3509 * @return bool true if the parsing is successful, false the parsing fails
3510 */
3511inline static bool parsePostCode(const std::string& postCodeID,
3512 uint64_t& currentValue, uint16_t& index)
3513{
3514 std::vector<std::string> split;
3515 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3516 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3517 {
3518 return false;
3519 }
3520
Ed Tanousca45aa32022-01-07 09:28:45 -08003521 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003522 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003523 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003524 const char* end = split[0].data() + split[0].size();
3525 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3526
3527 if (ptrIndex != end || ecIndex != std::errc())
3528 {
3529 return false;
3530 }
3531
3532 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003533
3534 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003535 end = split[1].data() + split[1].size();
3536 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003537
Ed Tanousdcf2ebc2022-01-25 10:07:45 -08003538 return ptrValue == end && ecValue != std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003539}
3540
3541inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3542{
George Liu0fda0f12021-11-16 10:06:17 +08003543 BMCWEB_ROUTE(
3544 app,
3545 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003546 .privileges(redfish::privileges::getLogEntry)
3547 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003548 [&app](const crow::Request& req,
3549 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3550 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003551 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003552 {
3553 return;
3554 }
3555 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3556 {
3557 asyncResp->res.result(boost::beast::http::status::bad_request);
3558 return;
3559 }
George Liu647b3cd2021-07-05 12:43:56 +08003560
Ed Tanous002d39b2022-05-31 08:59:27 -07003561 uint64_t currentValue = 0;
3562 uint16_t index = 0;
3563 if (!parsePostCode(postCodeID, currentValue, index))
3564 {
3565 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3566 return;
3567 }
George Liu647b3cd2021-07-05 12:43:56 +08003568
Ed Tanous002d39b2022-05-31 08:59:27 -07003569 crow::connections::systemBus->async_method_call(
3570 [asyncResp, postCodeID, currentValue](
3571 const boost::system::error_code ec,
3572 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3573 postcodes) {
3574 if (ec.value() == EBADR)
3575 {
3576 messages::resourceNotFound(asyncResp->res, "LogEntry",
3577 postCodeID);
3578 return;
3579 }
3580 if (ec)
3581 {
3582 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3583 messages::internalError(asyncResp->res);
3584 return;
3585 }
George Liu647b3cd2021-07-05 12:43:56 +08003586
Ed Tanous002d39b2022-05-31 08:59:27 -07003587 size_t value = static_cast<size_t>(currentValue) - 1;
3588 if (value == std::string::npos || postcodes.size() < currentValue)
3589 {
3590 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3591 messages::resourceNotFound(asyncResp->res, "LogEntry",
3592 postCodeID);
3593 return;
3594 }
George Liu647b3cd2021-07-05 12:43:56 +08003595
Ed Tanous002d39b2022-05-31 08:59:27 -07003596 const auto& [tID, c] = postcodes[value];
3597 if (c.empty())
3598 {
3599 BMCWEB_LOG_INFO << "No found post code data";
3600 messages::resourceNotFound(asyncResp->res, "LogEntry",
3601 postCodeID);
3602 return;
3603 }
3604 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3605 const char* d = reinterpret_cast<const char*>(c.data());
3606 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003607
Ed Tanous002d39b2022-05-31 08:59:27 -07003608 asyncResp->res.addHeader("Content-Type",
3609 "application/octet-stream");
3610 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
3611 asyncResp->res.body() = crow::utility::base64encode(strData);
3612 },
3613 "xyz.openbmc_project.State.Boot.PostCode0",
3614 "/xyz/openbmc_project/State/Boot/PostCode0",
3615 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3616 });
George Liu647b3cd2021-07-05 12:43:56 +08003617}
3618
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003619inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003620{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003621 BMCWEB_ROUTE(
3622 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003623 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003624 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003625 [&app](const crow::Request& req,
3626 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3627 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003628 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003629 {
3630 return;
3631 }
3632 uint16_t bootIndex = 0;
3633 uint64_t codeIndex = 0;
3634 if (!parsePostCode(targetID, codeIndex, bootIndex))
3635 {
3636 // Requested ID was not found
3637 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3638 return;
3639 }
3640 if (bootIndex == 0 || codeIndex == 0)
3641 {
3642 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3643 << targetID;
3644 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003645
Ed Tanous002d39b2022-05-31 08:59:27 -07003646 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3647 asyncResp->res.jsonValue["@odata.id"] =
3648 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3649 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3650 asyncResp->res.jsonValue["Description"] =
3651 "Collection of POST Code Log Entries";
3652 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3653 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003654
Ed Tanous002d39b2022-05-31 08:59:27 -07003655 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3656 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003657}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003658
Ed Tanous1da66f72018-07-27 16:13:37 -07003659} // namespace redfish