blob: 3b1da0ecef43ca2210883f10f87c67c1accaa6e1 [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Spencer Kub7028eb2021-10-26 15:27:35 +080018#include "gzfile.hpp"
George Liu647b3cd2021-07-05 12:43:56 +080019#include "http_utility.hpp"
Spencer Kub7028eb2021-10-26 15:27:35 +080020#include "human_sort.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070021#include "registries.hpp"
22#include "registries/base_message_registry.hpp"
23#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080024#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070025
Jason M. Billse1f26342018-07-18 12:12:00 -070026#include <systemd/sd-journal.h>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060027#include <unistd.h>
Jason M. Billse1f26342018-07-18 12:12:00 -070028
John Edward Broadbent7e860f12021-04-08 15:57:16 -070029#include <app.hpp>
Ed Tanous11ba3972022-07-11 09:50:41 -070030#include <boost/algorithm/string/classification.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060031#include <boost/algorithm/string/replace.hpp>
Jason M. Bills4851d452019-03-28 11:27:48 -070032#include <boost/algorithm/string/split.hpp>
Ed Tanous07c8c202022-07-11 10:08:08 -070033#include <boost/beast/http/verb.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070034#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080035#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080036#include <dbus_utility.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070037#include <error_messages.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070038#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070039#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040
George Liu647b3cd2021-07-05 12:43:56 +080041#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070042#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080043#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070044#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070045#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080046#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070047
48namespace redfish
49{
50
Gunnar Mills1214b7e2020-06-04 10:11:30 -050051constexpr char const* crashdumpObject = "com.intel.crashdump";
52constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050053constexpr char const* crashdumpInterface = "com.intel.crashdump";
54constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070055 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050056constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070057 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070058constexpr char const* crashdumpTelemetryInterface =
59 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070060
Ed Tanousfffb8c12022-02-07 23:53:03 -080061namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -070062{
Ed Tanous26702d02021-11-03 15:02:33 -070063static const Message*
64 getMessageFromRegistry(const std::string& messageKey,
65 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070066{
Ed Tanous002d39b2022-05-31 08:59:27 -070067 std::span<const MessageEntry>::iterator messageIt =
68 std::find_if(registry.begin(), registry.end(),
69 [&messageKey](const MessageEntry& messageEntry) {
70 return std::strcmp(messageEntry.first, messageKey.c_str()) == 0;
Ed Tanous26702d02021-11-03 15:02:33 -070071 });
72 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070073 {
74 return &messageIt->second;
75 }
76
77 return nullptr;
78}
79
Gunnar Mills1214b7e2020-06-04 10:11:30 -050080static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070081{
82 // Redfish MessageIds are in the form
83 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
84 // the right Message
85 std::vector<std::string> fields;
86 fields.reserve(4);
87 boost::split(fields, messageID, boost::is_any_of("."));
Ed Tanous02cad962022-06-30 16:50:15 -070088 const std::string& registryName = fields[0];
89 const std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070090
91 // Find the right registry and check it for the MessageKey
92 if (std::string(base::header.registryPrefix) == registryName)
93 {
94 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070095 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070096 }
97 if (std::string(openbmc::header.registryPrefix) == registryName)
98 {
99 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -0700100 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -0700101 }
102 return nullptr;
103}
Ed Tanousfffb8c12022-02-07 23:53:03 -0800104} // namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -0700105
James Feistf6150402019-01-08 10:36:20 -0800106namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700107
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500108inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700109{
Ed Tanousd4d25792020-09-29 15:15:03 -0700110 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
111 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
112 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
113 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700114 {
115 return "Critical";
116 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700117 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
118 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
119 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700120 {
121 return "OK";
122 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700123 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700124 {
125 return "Warning";
126 }
127 return "";
128}
129
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700130inline static int getJournalMetadata(sd_journal* journal,
131 const std::string_view& field,
132 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700133{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500134 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700135 size_t length = 0;
136 int ret = 0;
137 // Get the metadata from the requested field of the journal entry
Ed Tanous46ff87b2022-01-07 09:25:51 -0800138 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
139 const void** dataVoid = reinterpret_cast<const void**>(&data);
140
141 ret = sd_journal_get_data(journal, field.data(), dataVoid, &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700142 if (ret < 0)
143 {
144 return ret;
145 }
Ed Tanous39e77502019-03-04 17:35:53 -0800146 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700147 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000148 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700149 return ret;
150}
151
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700152inline static int getJournalMetadata(sd_journal* journal,
153 const std::string_view& field,
154 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700155{
156 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800157 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700158 // Get the metadata from the requested field of the journal entry
159 ret = getJournalMetadata(journal, field, metadata);
160 if (ret < 0)
161 {
162 return ret;
163 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000164 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700165 return ret;
166}
167
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700168inline static bool getEntryTimestamp(sd_journal* journal,
169 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800170{
171 int ret = 0;
172 uint64_t timestamp = 0;
173 ret = sd_journal_get_realtime_usec(journal, &timestamp);
174 if (ret < 0)
175 {
176 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
177 << strerror(-ret);
178 return false;
179 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800180 entryTimestamp = crow::utility::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500181 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800182}
Ed Tanous50b8a432022-02-03 16:29:50 -0800183
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700184inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
185 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700186{
187 int ret = 0;
188 static uint64_t prevTs = 0;
189 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700190 if (firstEntry)
191 {
192 prevTs = 0;
193 }
194
Jason M. Bills16428a12018-11-02 12:42:29 -0700195 // Get the entry timestamp
196 uint64_t curTs = 0;
197 ret = sd_journal_get_realtime_usec(journal, &curTs);
198 if (ret < 0)
199 {
200 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
201 << strerror(-ret);
202 return false;
203 }
204 // If the timestamp isn't unique, increment the index
205 if (curTs == prevTs)
206 {
207 index++;
208 }
209 else
210 {
211 // Otherwise, reset it
212 index = 0;
213 }
214 // Save the timestamp
215 prevTs = curTs;
216
217 entryID = std::to_string(curTs);
218 if (index > 0)
219 {
220 entryID += "_" + std::to_string(index);
221 }
222 return true;
223}
224
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500225static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700226 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700227{
Ed Tanous271584a2019-07-09 16:24:22 -0700228 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700229 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700230 if (firstEntry)
231 {
232 prevTs = 0;
233 }
234
Jason M. Bills95820182019-04-22 16:25:34 -0700235 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700236 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700237 std::tm timeStruct = {};
238 std::istringstream entryStream(logEntry);
239 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
240 {
241 curTs = std::mktime(&timeStruct);
242 }
243 // If the timestamp isn't unique, increment the index
244 if (curTs == prevTs)
245 {
246 index++;
247 }
248 else
249 {
250 // Otherwise, reset it
251 index = 0;
252 }
253 // Save the timestamp
254 prevTs = curTs;
255
256 entryID = std::to_string(curTs);
257 if (index > 0)
258 {
259 entryID += "_" + std::to_string(index);
260 }
261 return true;
262}
263
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700264inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800265 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
266 const std::string& entryID, uint64_t& timestamp,
267 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700268{
269 if (entryID.empty())
270 {
271 return false;
272 }
273 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800274 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700275
Ed Tanous81ce6092020-12-17 16:54:55 +0000276 auto underscorePos = tsStr.find('_');
Ed Tanous71d5d8d2022-01-25 11:04:33 -0800277 if (underscorePos != std::string_view::npos)
Jason M. Bills16428a12018-11-02 12:42:29 -0700278 {
279 // Timestamp has an index
280 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800281 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700282 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700283 auto [ptr, ec] = std::from_chars(
284 indexStr.data(), indexStr.data() + indexStr.size(), index);
285 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700286 {
Ed Tanousace85d62021-10-26 12:45:59 -0700287 messages::resourceMissingAtURI(
288 asyncResp->res, crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700289 return false;
290 }
291 }
292 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700293 auto [ptr, ec] =
294 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
295 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700296 {
Ed Tanousace85d62021-10-26 12:45:59 -0700297 messages::resourceMissingAtURI(asyncResp->res,
298 crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700299 return false;
300 }
301 return true;
302}
303
Jason M. Bills95820182019-04-22 16:25:34 -0700304static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500305 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700306{
307 static const std::filesystem::path redfishLogDir = "/var/log";
308 static const std::string redfishLogFilename = "redfish";
309
310 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500311 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700312 std::filesystem::directory_iterator(redfishLogDir))
313 {
314 // If we find a redfish log file, save the path
315 std::string filename = dirEnt.path().filename();
Ed Tanous11ba3972022-07-11 09:50:41 -0700316 if (filename.starts_with(redfishLogFilename))
Jason M. Bills95820182019-04-22 16:25:34 -0700317 {
318 redfishLogFiles.emplace_back(redfishLogDir / filename);
319 }
320 }
321 // As the log files rotate, they are appended with a ".#" that is higher for
322 // the older logs. Since we don't expect more than 10 log files, we
323 // can just sort the list to get them in order from newest to oldest
324 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
325
326 return !redfishLogFiles.empty();
327}
328
Nan Zhou21ab4042022-06-26 23:07:40 +0000329static std::string getDumpEntriesPath(const std::string& dumpType)
Claire Weinanfdd26902022-03-01 14:18:25 -0800330{
331 std::string entriesPath;
332
333 if (dumpType == "BMC")
334 {
335 entriesPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
336 }
337 else if (dumpType == "FaultLog")
338 {
339 entriesPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/";
340 }
341 else if (dumpType == "System")
342 {
343 entriesPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
344 }
345 else
346 {
347 BMCWEB_LOG_ERROR << "getDumpEntriesPath() invalid dump type: "
348 << dumpType;
349 }
350
351 // Returns empty string on error
352 return entriesPath;
353}
354
zhanghch058d1b46d2021-04-01 11:18:24 +0800355inline void
356 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
357 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500358{
Claire Weinanfdd26902022-03-01 14:18:25 -0800359 std::string entriesPath = getDumpEntriesPath(dumpType);
360 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500361 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500362 messages::internalError(asyncResp->res);
363 return;
364 }
365
366 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800367 [asyncResp, entriesPath,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800368 dumpType](const boost::system::error_code ec,
369 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700370 if (ec)
371 {
372 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
373 messages::internalError(asyncResp->res);
374 return;
375 }
376
Claire Weinanfdd26902022-03-01 14:18:25 -0800377 // Remove ending slash
378 std::string odataIdStr = entriesPath;
379 if (!odataIdStr.empty())
380 {
381 odataIdStr.pop_back();
382 }
383
384 asyncResp->res.jsonValue["@odata.type"] =
385 "#LogEntryCollection.LogEntryCollection";
386 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
387 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
388 asyncResp->res.jsonValue["Description"] =
389 "Collection of " + dumpType + " Dump Entries";
390
Ed Tanous002d39b2022-05-31 08:59:27 -0700391 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
392 entriesArray = nlohmann::json::array();
393 std::string dumpEntryPath =
394 "/xyz/openbmc_project/dump/" +
395 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
396
397 std::sort(resp.begin(), resp.end(), [](const auto& l, const auto& r) {
398 return AlphanumLess<std::string>()(l.first.filename(),
399 r.first.filename());
400 });
401
402 for (auto& object : resp)
403 {
404 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500405 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700406 continue;
407 }
408 uint64_t timestamp = 0;
409 uint64_t size = 0;
410 std::string dumpStatus;
Jason M. Bills433b68b2022-06-28 12:24:26 -0700411 nlohmann::json::object_t thisEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -0700412
413 std::string entryID = object.first.filename();
414 if (entryID.empty())
415 {
416 continue;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500417 }
418
Ed Tanous002d39b2022-05-31 08:59:27 -0700419 for (auto& interfaceMap : object.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500420 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700421 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500422 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700423 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500424 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700425 if (propertyMap.first == "Status")
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500426 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700427 const auto* status =
428 std::get_if<std::string>(&propertyMap.second);
429 if (status == nullptr)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500430 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700431 messages::internalError(asyncResp->res);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500432 break;
433 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700434 dumpStatus = *status;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500435 }
436 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700437 }
438 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
439 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500440
Ed Tanous002d39b2022-05-31 08:59:27 -0700441 for (auto& propertyMap : interfaceMap.second)
442 {
443 if (propertyMap.first == "Size")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500444 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700445 const auto* sizePtr =
446 std::get_if<uint64_t>(&propertyMap.second);
447 if (sizePtr == nullptr)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500448 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700449 messages::internalError(asyncResp->res);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500450 break;
451 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700452 size = *sizePtr;
453 break;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500454 }
455 }
456 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700457 else if (interfaceMap.first ==
458 "xyz.openbmc_project.Time.EpochTime")
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500459 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500460
Ed Tanous002d39b2022-05-31 08:59:27 -0700461 for (const auto& propertyMap : interfaceMap.second)
462 {
463 if (propertyMap.first == "Elapsed")
464 {
465 const uint64_t* usecsTimeStamp =
466 std::get_if<uint64_t>(&propertyMap.second);
467 if (usecsTimeStamp == nullptr)
468 {
469 messages::internalError(asyncResp->res);
470 break;
471 }
472 timestamp = (*usecsTimeStamp / 1000 / 1000);
473 break;
474 }
475 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500476 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500477 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700478
479 if (dumpStatus !=
480 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
481 !dumpStatus.empty())
482 {
483 // Dump status is not Complete, no need to enumerate
484 continue;
485 }
486
487 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800488 thisEntry["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700489 thisEntry["Id"] = entryID;
490 thisEntry["EntryType"] = "Event";
491 thisEntry["Created"] = crow::utility::getDateTimeUint(timestamp);
492 thisEntry["Name"] = dumpType + " Dump Entry";
493
Ed Tanous002d39b2022-05-31 08:59:27 -0700494 if (dumpType == "BMC")
495 {
496 thisEntry["DiagnosticDataType"] = "Manager";
497 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800498 entriesPath + entryID + "/attachment";
499 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700500 }
501 else if (dumpType == "System")
502 {
503 thisEntry["DiagnosticDataType"] = "OEM";
504 thisEntry["OEMDiagnosticDataType"] = "System";
505 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800506 entriesPath + entryID + "/attachment";
507 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700508 }
509 entriesArray.push_back(std::move(thisEntry));
510 }
511 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500512 },
513 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
514 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
515}
516
zhanghch058d1b46d2021-04-01 11:18:24 +0800517inline void
Claire Weinanc7a6d662022-06-13 16:36:39 -0700518 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800519 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500520{
Claire Weinanfdd26902022-03-01 14:18:25 -0800521 std::string entriesPath = getDumpEntriesPath(dumpType);
522 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500523 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500524 messages::internalError(asyncResp->res);
525 return;
526 }
527
528 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800529 [asyncResp, entryID, dumpType,
530 entriesPath](const boost::system::error_code ec,
Ed Tanous02cad962022-06-30 16:50:15 -0700531 const dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700532 if (ec)
533 {
534 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
535 messages::internalError(asyncResp->res);
536 return;
537 }
538
539 bool foundDumpEntry = false;
540 std::string dumpEntryPath =
541 "/xyz/openbmc_project/dump/" +
542 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
543
544 for (const auto& objectPath : resp)
545 {
546 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500547 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700548 continue;
549 }
550
551 foundDumpEntry = true;
552 uint64_t timestamp = 0;
553 uint64_t size = 0;
554 std::string dumpStatus;
555
556 for (const auto& interfaceMap : objectPath.second)
557 {
558 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
559 {
560 for (const auto& propertyMap : interfaceMap.second)
561 {
562 if (propertyMap.first == "Status")
563 {
564 const std::string* status =
565 std::get_if<std::string>(&propertyMap.second);
566 if (status == nullptr)
567 {
568 messages::internalError(asyncResp->res);
569 break;
570 }
571 dumpStatus = *status;
572 }
573 }
574 }
575 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
576 {
577 for (const auto& propertyMap : interfaceMap.second)
578 {
579 if (propertyMap.first == "Size")
580 {
581 const uint64_t* sizePtr =
582 std::get_if<uint64_t>(&propertyMap.second);
583 if (sizePtr == nullptr)
584 {
585 messages::internalError(asyncResp->res);
586 break;
587 }
588 size = *sizePtr;
589 break;
590 }
591 }
592 }
593 else if (interfaceMap.first ==
594 "xyz.openbmc_project.Time.EpochTime")
595 {
596 for (const auto& propertyMap : interfaceMap.second)
597 {
598 if (propertyMap.first == "Elapsed")
599 {
600 const uint64_t* usecsTimeStamp =
601 std::get_if<uint64_t>(&propertyMap.second);
602 if (usecsTimeStamp == nullptr)
603 {
604 messages::internalError(asyncResp->res);
605 break;
606 }
607 timestamp = *usecsTimeStamp / 1000 / 1000;
608 break;
609 }
610 }
611 }
612 }
613
614 if (dumpStatus !=
615 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
616 !dumpStatus.empty())
617 {
618 // Dump status is not Complete
619 // return not found until status is changed to Completed
620 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
621 entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500622 return;
623 }
624
Ed Tanous002d39b2022-05-31 08:59:27 -0700625 asyncResp->res.jsonValue["@odata.type"] =
626 "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800627 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700628 asyncResp->res.jsonValue["Id"] = entryID;
629 asyncResp->res.jsonValue["EntryType"] = "Event";
630 asyncResp->res.jsonValue["Created"] =
631 crow::utility::getDateTimeUint(timestamp);
632 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500633
Ed Tanous002d39b2022-05-31 08:59:27 -0700634 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500635 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700636 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
637 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800638 entriesPath + entryID + "/attachment";
639 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500640 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700641 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500642 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700643 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
644 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
645 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800646 entriesPath + entryID + "/attachment";
647 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500648 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700649 }
650 if (!foundDumpEntry)
651 {
652 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
653 messages::internalError(asyncResp->res);
654 return;
655 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500656 },
657 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
658 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
659}
660
zhanghch058d1b46d2021-04-01 11:18:24 +0800661inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800662 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500663 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500664{
Ed Tanous002d39b2022-05-31 08:59:27 -0700665 auto respHandler =
666 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500667 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
668 if (ec)
669 {
George Liu3de8d8b2021-03-22 17:49:39 +0800670 if (ec.value() == EBADR)
671 {
672 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
673 return;
674 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500675 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -0800676 << ec << " entryID=" << entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500677 messages::internalError(asyncResp->res);
678 return;
679 }
680 };
681 crow::connections::systemBus->async_method_call(
682 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500683 "/xyz/openbmc_project/dump/" +
684 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
685 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500686 "xyz.openbmc_project.Object.Delete", "Delete");
687}
688
zhanghch058d1b46d2021-04-01 11:18:24 +0800689inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700690 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800691 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
692 const uint32_t& dumpId, const std::string& dumpPath,
693 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500694{
695 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -0500696 [dumpId, dumpPath,
697 dumpType](boost::system::error_code err, sdbusplus::message_t& m,
698 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700699 if (err)
700 {
701 BMCWEB_LOG_ERROR << "Error in creating a dump";
702 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500703 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700704 }
705
706 dbus::utility::DBusInteracesMap interfacesList;
707
708 sdbusplus::message::object_path objPath;
709
710 m.read(objPath, interfacesList);
711
712 if (objPath.str ==
713 "/xyz/openbmc_project/dump/" +
714 std::string(boost::algorithm::to_lower_copy(dumpType)) +
715 "/entry/" + std::to_string(dumpId))
716 {
717 nlohmann::json retMessage = messages::success();
718 taskData->messages.emplace_back(retMessage);
719
720 std::string headerLoc =
721 "Location: " + dumpPath + std::to_string(dumpId);
722 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
723
724 taskData->state = "Completed";
725 return task::completed;
726 }
727 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500728 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800729 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500730 "member='InterfacesAdded', "
731 "path='/xyz/openbmc_project/dump'");
732
733 task->startTimer(std::chrono::minutes(3));
734 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700735 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500736}
737
zhanghch058d1b46d2021-04-01 11:18:24 +0800738inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
739 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500740{
Claire Weinanfdd26902022-03-01 14:18:25 -0800741 std::string dumpPath = getDumpEntriesPath(dumpType);
742 if (dumpPath.empty())
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500743 {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500744 messages::internalError(asyncResp->res);
745 return;
746 }
747
748 std::optional<std::string> diagnosticDataType;
749 std::optional<std::string> oemDiagnosticDataType;
750
Willy Tu15ed6782021-12-14 11:03:16 -0800751 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500752 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
753 "OEMDiagnosticDataType", oemDiagnosticDataType))
754 {
755 return;
756 }
757
758 if (dumpType == "System")
759 {
760 if (!oemDiagnosticDataType || !diagnosticDataType)
761 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800762 BMCWEB_LOG_ERROR
763 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500764 messages::actionParameterMissing(
765 asyncResp->res, "CollectDiagnosticData",
766 "DiagnosticDataType & OEMDiagnosticDataType");
767 return;
768 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700769 if ((*oemDiagnosticDataType != "System") ||
770 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500771 {
772 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700773 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500774 return;
775 }
776 }
777 else if (dumpType == "BMC")
778 {
779 if (!diagnosticDataType)
780 {
George Liu0fda0f12021-11-16 10:06:17 +0800781 BMCWEB_LOG_ERROR
782 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500783 messages::actionParameterMissing(
784 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
785 return;
786 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700787 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500788 {
789 BMCWEB_LOG_ERROR
790 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700791 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500792 return;
793 }
794 }
795
796 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700797 [asyncResp, payload(task::Payload(req)), dumpPath,
798 dumpType](const boost::system::error_code ec,
799 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700800 if (ec)
801 {
802 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
803 messages::internalError(asyncResp->res);
804 return;
805 }
806 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500807
Ed Tanous002d39b2022-05-31 08:59:27 -0700808 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
809 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500810 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500811 "xyz.openbmc_project.Dump.Manager",
812 "/xyz/openbmc_project/dump/" +
813 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500814 "xyz.openbmc_project.Dump.Create", "CreateDump");
815}
816
zhanghch058d1b46d2021-04-01 11:18:24 +0800817inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
818 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500819{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500820 std::string dumpTypeLowerCopy =
821 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800822
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500823 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800824 [asyncResp, dumpType](
825 const boost::system::error_code ec,
826 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700827 if (ec)
828 {
829 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
830 messages::internalError(asyncResp->res);
831 return;
832 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500833
Ed Tanous002d39b2022-05-31 08:59:27 -0700834 for (const std::string& path : subTreePaths)
835 {
836 sdbusplus::message::object_path objPath(path);
837 std::string logID = objPath.filename();
838 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500839 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700840 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500841 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700842 deleteDumpEntry(asyncResp, logID, dumpType);
843 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500844 },
845 "xyz.openbmc_project.ObjectMapper",
846 "/xyz/openbmc_project/object_mapper",
847 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500848 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
849 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
850 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500851}
852
Ed Tanousb9d36b42022-02-26 21:42:46 -0800853inline static void
854 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
855 std::string& filename, std::string& timestamp,
856 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700857{
858 for (auto property : params)
859 {
860 if (property.first == "Timestamp")
861 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500862 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500863 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700864 if (value != nullptr)
865 {
866 timestamp = *value;
867 }
868 }
869 else if (property.first == "Filename")
870 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500871 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500872 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700873 if (value != nullptr)
874 {
875 filename = *value;
876 }
877 }
878 else if (property.first == "Log")
879 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500880 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500881 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700882 if (value != nullptr)
883 {
884 logfile = *value;
885 }
886 }
887 }
888}
889
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500890constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700891inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700892{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800893 /**
894 * Functions triggers appropriate requests on DBus
895 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700896 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700897 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700898 .methods(boost::beast::http::verb::get)(
899 [&app](const crow::Request& req,
900 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000901 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700902 {
903 return;
904 }
905 // Collections don't include the static data added by SubRoute
906 // because it has a duplicate entry for members
907 asyncResp->res.jsonValue["@odata.type"] =
908 "#LogServiceCollection.LogServiceCollection";
909 asyncResp->res.jsonValue["@odata.id"] =
910 "/redfish/v1/Systems/system/LogServices";
911 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
912 asyncResp->res.jsonValue["Description"] =
913 "Collection of LogServices for this Computer System";
914 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
915 logServiceArray = nlohmann::json::array();
916 nlohmann::json::object_t eventLog;
917 eventLog["@odata.id"] =
918 "/redfish/v1/Systems/system/LogServices/EventLog";
919 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500920#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700921 nlohmann::json::object_t dumpLog;
922 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
923 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600924#endif
925
Jason M. Billsd53dd412019-02-12 17:16:22 -0800926#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700927 nlohmann::json::object_t crashdump;
928 crashdump["@odata.id"] =
929 "/redfish/v1/Systems/system/LogServices/Crashdump";
930 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800931#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800932
933#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700934 nlohmann::json::object_t hostlogger;
935 hostlogger["@odata.id"] =
936 "/redfish/v1/Systems/system/LogServices/HostLogger";
937 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800938#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700939 asyncResp->res.jsonValue["Members@odata.count"] =
940 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800941
Ed Tanous002d39b2022-05-31 08:59:27 -0700942 crow::connections::systemBus->async_method_call(
943 [asyncResp](const boost::system::error_code ec,
944 const dbus::utility::MapperGetSubTreePathsResponse&
945 subtreePath) {
946 if (ec)
947 {
948 BMCWEB_LOG_ERROR << ec;
949 return;
950 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700951
Ed Tanous002d39b2022-05-31 08:59:27 -0700952 for (const auto& pathStr : subtreePath)
953 {
954 if (pathStr.find("PostCode") != std::string::npos)
955 {
956 nlohmann::json& logServiceArrayLocal =
957 asyncResp->res.jsonValue["Members"];
958 logServiceArrayLocal.push_back(
959 {{"@odata.id",
960 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
961 asyncResp->res.jsonValue["Members@odata.count"] =
962 logServiceArrayLocal.size();
963 return;
964 }
965 }
966 },
967 "xyz.openbmc_project.ObjectMapper",
968 "/xyz/openbmc_project/object_mapper",
969 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
970 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700971 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700972}
973
974inline void requestRoutesEventLogService(App& app)
975{
976 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700977 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700978 .methods(boost::beast::http::verb::get)(
979 [&app](const crow::Request& req,
980 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000981 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700982 {
983 return;
984 }
985 asyncResp->res.jsonValue["@odata.id"] =
986 "/redfish/v1/Systems/system/LogServices/EventLog";
987 asyncResp->res.jsonValue["@odata.type"] =
988 "#LogService.v1_1_0.LogService";
989 asyncResp->res.jsonValue["Name"] = "Event Log Service";
990 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
991 asyncResp->res.jsonValue["Id"] = "EventLog";
992 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530993
Ed Tanous002d39b2022-05-31 08:59:27 -0700994 std::pair<std::string, std::string> redfishDateTimeOffset =
995 crow::utility::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530996
Ed Tanous002d39b2022-05-31 08:59:27 -0700997 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
998 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
999 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05301000
Ed Tanous002d39b2022-05-31 08:59:27 -07001001 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1002 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1003 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001004
Ed Tanous002d39b2022-05-31 08:59:27 -07001005 {"target",
1006 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001007 });
1008}
1009
1010inline void requestRoutesJournalEventLogClear(App& app)
1011{
Jason M. Bills4978b632022-02-22 14:17:43 -08001012 BMCWEB_ROUTE(
1013 app,
1014 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001015 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001017 [&app](const crow::Request& req,
1018 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001019 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001020 {
1021 return;
1022 }
1023 // Clear the EventLog by deleting the log files
1024 std::vector<std::filesystem::path> redfishLogFiles;
1025 if (getRedfishLogFiles(redfishLogFiles))
1026 {
1027 for (const std::filesystem::path& file : redfishLogFiles)
1028 {
1029 std::error_code ec;
1030 std::filesystem::remove(file, ec);
1031 }
1032 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001033
Ed Tanous002d39b2022-05-31 08:59:27 -07001034 // Reload rsyslog so it knows to start new log files
1035 crow::connections::systemBus->async_method_call(
1036 [asyncResp](const boost::system::error_code ec) {
1037 if (ec)
1038 {
1039 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1040 messages::internalError(asyncResp->res);
1041 return;
1042 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001043
Ed Tanous002d39b2022-05-31 08:59:27 -07001044 messages::success(asyncResp->res);
1045 },
1046 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1047 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1048 "replace");
1049 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001050}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001051
Jason M. Billsac992cd2022-06-24 13:31:46 -07001052enum class LogParseError
1053{
1054 success,
1055 parseFailed,
1056 messageIdNotInRegistry,
1057};
1058
1059static LogParseError
1060 fillEventLogEntryJson(const std::string& logEntryID,
1061 const std::string& logEntry,
1062 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001063{
Jason M. Bills95820182019-04-22 16:25:34 -07001064 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001065 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001066 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001067 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001068 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001069 return LogParseError::parseFailed;
Jason M. Bills95820182019-04-22 16:25:34 -07001070 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001071 std::string timestamp = logEntry.substr(0, space);
1072 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001073 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001074 if (entryStart == std::string::npos)
1075 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001076 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001077 }
1078 std::string_view entry(logEntry);
1079 entry.remove_prefix(entryStart);
1080 // Use split to separate the entry into its fields
1081 std::vector<std::string> logEntryFields;
1082 boost::split(logEntryFields, entry, boost::is_any_of(","),
1083 boost::token_compress_on);
1084 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001085 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001086 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001087 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001088 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001089 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001090
Jason M. Bills4851d452019-03-28 11:27:48 -07001091 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001092 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001093
Sui Chen54417b02022-03-24 14:59:52 -07001094 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001095 {
Sui Chen54417b02022-03-24 14:59:52 -07001096 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001097 return LogParseError::messageIdNotInRegistry;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001098 }
1099
Sui Chen54417b02022-03-24 14:59:52 -07001100 std::string msg = message->message;
1101
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001102 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001103 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001104 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001105 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001106 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001107 // If the first string is empty, assume there are no MessageArgs
1108 std::size_t messageArgsSize = 0;
1109 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001110 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001111 messageArgsSize = logEntryFields.size() - 1;
1112 }
1113
Ed Tanous23a21a12020-07-25 04:45:05 +00001114 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001115
1116 // Fill the MessageArgs into the Message
1117 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001118 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001119 {
1120 std::string argStr = "%" + std::to_string(++i);
1121 size_t argPos = msg.find(argStr);
1122 if (argPos != std::string::npos)
1123 {
1124 msg.replace(argPos, argStr.length(), messageArg);
1125 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001126 }
1127 }
1128
Jason M. Bills95820182019-04-22 16:25:34 -07001129 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1130 // format which matches the Redfish format except for the fractional seconds
1131 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001132 std::size_t dot = timestamp.find_first_of('.');
1133 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001134 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001135 {
Jason M. Bills95820182019-04-22 16:25:34 -07001136 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001137 }
1138
1139 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07001140 logEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1141 logEntryJson["@odata.id"] =
1142 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1143 logEntryJson["Name"] = "System Event Log Entry";
1144 logEntryJson["Id"] = logEntryID;
1145 logEntryJson["Message"] = std::move(msg);
1146 logEntryJson["MessageId"] = std::move(messageID);
1147 logEntryJson["MessageArgs"] = messageArgs;
1148 logEntryJson["EntryType"] = "Event";
1149 logEntryJson["Severity"] = message->messageSeverity;
1150 logEntryJson["Created"] = std::move(timestamp);
Jason M. Billsac992cd2022-06-24 13:31:46 -07001151 return LogParseError::success;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001152}
1153
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001154inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001155{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001156 BMCWEB_ROUTE(app,
1157 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001158 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001159 .methods(boost::beast::http::verb::get)(
1160 [&app](const crow::Request& req,
1161 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1162 query_param::QueryCapabilities capabilities = {
1163 .canDelegateTop = true,
1164 .canDelegateSkip = true,
1165 };
1166 query_param::Query delegatedQuery;
1167 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001168 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001169 {
1170 return;
1171 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001172 size_t top =
1173 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
1174 size_t skip = delegatedQuery.skip.value_or(0);
1175
Ed Tanous002d39b2022-05-31 08:59:27 -07001176 // Collections don't include the static data added by SubRoute
1177 // because it has a duplicate entry for members
1178 asyncResp->res.jsonValue["@odata.type"] =
1179 "#LogEntryCollection.LogEntryCollection";
1180 asyncResp->res.jsonValue["@odata.id"] =
1181 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1182 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1183 asyncResp->res.jsonValue["Description"] =
1184 "Collection of System Event Log Entries";
1185
1186 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1187 logEntryArray = nlohmann::json::array();
1188 // Go through the log files and create a unique ID for each
1189 // entry
1190 std::vector<std::filesystem::path> redfishLogFiles;
1191 getRedfishLogFiles(redfishLogFiles);
1192 uint64_t entryCount = 0;
1193 std::string logEntry;
1194
1195 // Oldest logs are in the last file, so start there and loop
1196 // backwards
1197 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1198 it++)
1199 {
1200 std::ifstream logStream(*it);
1201 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001202 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001203 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001204 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001205
Ed Tanous002d39b2022-05-31 08:59:27 -07001206 // Reset the unique ID on the first entry
1207 bool firstEntry = true;
1208 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001209 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001210 std::string idStr;
1211 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001212 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001213 continue;
1214 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001215 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001216
Jason M. Billsde703c52022-06-23 14:19:04 -07001217 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001218 LogParseError status =
1219 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1220 if (status == LogParseError::messageIdNotInRegistry)
1221 {
1222 continue;
1223 }
1224 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001225 {
1226 messages::internalError(asyncResp->res);
1227 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001228 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001229
Jason M. Billsde703c52022-06-23 14:19:04 -07001230 entryCount++;
1231 // Handle paging using skip (number of entries to skip from the
1232 // start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07001233 if (entryCount <= skip || entryCount > skip + top)
Jason M. Billsde703c52022-06-23 14:19:04 -07001234 {
1235 continue;
1236 }
1237
1238 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001239 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001240 }
1241 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001242 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07001243 {
1244 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1245 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001246 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07001247 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001248 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001249}
Chicago Duan336e96c2019-07-15 14:22:08 +08001250
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001251inline void requestRoutesJournalEventLogEntry(App& app)
1252{
1253 BMCWEB_ROUTE(
1254 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001255 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001256 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001257 [&app](const crow::Request& req,
1258 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1259 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001260 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001261 {
1262 return;
1263 }
1264 const std::string& targetID = param;
1265
1266 // Go through the log files and check the unique ID for each
1267 // entry to find the target entry
1268 std::vector<std::filesystem::path> redfishLogFiles;
1269 getRedfishLogFiles(redfishLogFiles);
1270 std::string logEntry;
1271
1272 // Oldest logs are in the last file, so start there and loop
1273 // backwards
1274 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1275 it++)
1276 {
1277 std::ifstream logStream(*it);
1278 if (!logStream.is_open())
1279 {
1280 continue;
1281 }
1282
1283 // Reset the unique ID on the first entry
1284 bool firstEntry = true;
1285 while (std::getline(logStream, logEntry))
1286 {
1287 std::string idStr;
1288 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001289 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001290 continue;
1291 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001292 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001293
1294 if (idStr == targetID)
1295 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001296 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001297 LogParseError status =
1298 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1299 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001300 {
1301 messages::internalError(asyncResp->res);
1302 return;
1303 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07001304 asyncResp->res.jsonValue.update(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001305 return;
1306 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001307 }
1308 }
1309 // Requested ID was not found
1310 messages::resourceMissingAtURI(asyncResp->res,
1311 crow::utility::urlFromPieces(targetID));
1312 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001313}
1314
1315inline void requestRoutesDBusEventLogEntryCollection(App& app)
1316{
1317 BMCWEB_ROUTE(app,
1318 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001319 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001320 .methods(boost::beast::http::verb::get)(
1321 [&app](const crow::Request& req,
1322 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001323 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001324 {
1325 return;
1326 }
1327 // Collections don't include the static data added by SubRoute
1328 // because it has a duplicate entry for members
1329 asyncResp->res.jsonValue["@odata.type"] =
1330 "#LogEntryCollection.LogEntryCollection";
1331 asyncResp->res.jsonValue["@odata.id"] =
1332 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1333 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1334 asyncResp->res.jsonValue["Description"] =
1335 "Collection of System Event Log Entries";
1336
1337 // DBus implementation of EventLog/Entries
1338 // Make call to Logging Service to find all log entry objects
1339 crow::connections::systemBus->async_method_call(
1340 [asyncResp](const boost::system::error_code ec,
1341 const dbus::utility::ManagedObjectType& resp) {
1342 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001343 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001344 // TODO Handle for specific error code
1345 BMCWEB_LOG_ERROR
1346 << "getLogEntriesIfaceData resp_handler got error " << ec;
1347 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001348 return;
1349 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001350 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1351 entriesArray = nlohmann::json::array();
1352 for (const auto& objectPath : resp)
1353 {
1354 const uint32_t* id = nullptr;
1355 const uint64_t* timestamp = nullptr;
1356 const uint64_t* updateTimestamp = nullptr;
1357 const std::string* severity = nullptr;
1358 const std::string* message = nullptr;
1359 const std::string* filePath = nullptr;
1360 bool resolved = false;
1361 for (const auto& interfaceMap : objectPath.second)
1362 {
1363 if (interfaceMap.first ==
1364 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001365 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001366 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001367 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001368 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001369 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001370 id = std::get_if<uint32_t>(&propertyMap.second);
1371 }
1372 else if (propertyMap.first == "Timestamp")
1373 {
1374 timestamp =
1375 std::get_if<uint64_t>(&propertyMap.second);
1376 }
1377 else if (propertyMap.first == "UpdateTimestamp")
1378 {
1379 updateTimestamp =
1380 std::get_if<uint64_t>(&propertyMap.second);
1381 }
1382 else if (propertyMap.first == "Severity")
1383 {
1384 severity = std::get_if<std::string>(
1385 &propertyMap.second);
1386 }
1387 else if (propertyMap.first == "Message")
1388 {
1389 message = std::get_if<std::string>(
1390 &propertyMap.second);
1391 }
1392 else if (propertyMap.first == "Resolved")
1393 {
1394 const bool* resolveptr =
1395 std::get_if<bool>(&propertyMap.second);
1396 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001397 {
1398 messages::internalError(asyncResp->res);
1399 return;
1400 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001401 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001402 }
1403 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001404 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001405 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001406 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001407 messages::internalError(asyncResp->res);
1408 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001409 }
1410 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001411 else if (interfaceMap.first ==
1412 "xyz.openbmc_project.Common.FilePath")
1413 {
1414 for (const auto& propertyMap : interfaceMap.second)
1415 {
1416 if (propertyMap.first == "Path")
1417 {
1418 filePath = std::get_if<std::string>(
1419 &propertyMap.second);
1420 }
1421 }
1422 }
1423 }
1424 // Object path without the
1425 // xyz.openbmc_project.Logging.Entry interface, ignore
1426 // and continue.
1427 if (id == nullptr || message == nullptr ||
1428 severity == nullptr || timestamp == nullptr ||
1429 updateTimestamp == nullptr)
1430 {
1431 continue;
1432 }
1433 entriesArray.push_back({});
1434 nlohmann::json& thisEntry = entriesArray.back();
1435 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1436 thisEntry["@odata.id"] =
1437 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1438 std::to_string(*id);
1439 thisEntry["Name"] = "System Event Log Entry";
1440 thisEntry["Id"] = std::to_string(*id);
1441 thisEntry["Message"] = *message;
1442 thisEntry["Resolved"] = resolved;
1443 thisEntry["EntryType"] = "Event";
1444 thisEntry["Severity"] =
1445 translateSeverityDbusToRedfish(*severity);
1446 thisEntry["Created"] =
1447 crow::utility::getDateTimeUintMs(*timestamp);
1448 thisEntry["Modified"] =
1449 crow::utility::getDateTimeUintMs(*updateTimestamp);
1450 if (filePath != nullptr)
1451 {
1452 thisEntry["AdditionalDataURI"] =
1453 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1454 std::to_string(*id) + "/attachment";
1455 }
1456 }
1457 std::sort(
1458 entriesArray.begin(), entriesArray.end(),
1459 [](const nlohmann::json& left, const nlohmann::json& right) {
1460 return (left["Id"] <= right["Id"]);
1461 });
1462 asyncResp->res.jsonValue["Members@odata.count"] =
1463 entriesArray.size();
1464 },
1465 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1466 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001467 });
1468}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001469
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001470inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001471{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001472 BMCWEB_ROUTE(
1473 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001474 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001475 .methods(boost::beast::http::verb::get)(
1476 [&app](const crow::Request& req,
1477 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1478 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001479 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001480 {
1481 return;
1482 }
1483 std::string entryID = param;
1484 dbus::utility::escapePathForDbus(entryID);
1485
1486 // DBus implementation of EventLog/Entries
1487 // Make call to Logging Service to find all log entry objects
1488 crow::connections::systemBus->async_method_call(
1489 [asyncResp, entryID](const boost::system::error_code ec,
1490 const dbus::utility::DBusPropertiesMap& resp) {
1491 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001492 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001493 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1494 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001495 return;
1496 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001497 if (ec)
1498 {
1499 BMCWEB_LOG_ERROR
1500 << "EventLogEntry (DBus) resp_handler got error " << ec;
1501 messages::internalError(asyncResp->res);
1502 return;
1503 }
1504 const uint32_t* id = nullptr;
1505 const uint64_t* timestamp = nullptr;
1506 const uint64_t* updateTimestamp = nullptr;
1507 const std::string* severity = nullptr;
1508 const std::string* message = nullptr;
1509 const std::string* filePath = nullptr;
1510 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001511
Ed Tanous002d39b2022-05-31 08:59:27 -07001512 for (const auto& propertyMap : resp)
1513 {
1514 if (propertyMap.first == "Id")
1515 {
1516 id = std::get_if<uint32_t>(&propertyMap.second);
1517 }
1518 else if (propertyMap.first == "Timestamp")
1519 {
1520 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1521 }
1522 else if (propertyMap.first == "UpdateTimestamp")
1523 {
1524 updateTimestamp =
1525 std::get_if<uint64_t>(&propertyMap.second);
1526 }
1527 else if (propertyMap.first == "Severity")
1528 {
1529 severity = std::get_if<std::string>(&propertyMap.second);
1530 }
1531 else if (propertyMap.first == "Message")
1532 {
1533 message = std::get_if<std::string>(&propertyMap.second);
1534 }
1535 else if (propertyMap.first == "Resolved")
1536 {
1537 const bool* resolveptr =
1538 std::get_if<bool>(&propertyMap.second);
1539 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001540 {
1541 messages::internalError(asyncResp->res);
1542 return;
1543 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001544 resolved = *resolveptr;
1545 }
1546 else if (propertyMap.first == "Path")
1547 {
1548 filePath = std::get_if<std::string>(&propertyMap.second);
1549 }
1550 }
1551 if (id == nullptr || message == nullptr || severity == nullptr ||
1552 timestamp == nullptr || updateTimestamp == nullptr)
1553 {
1554 messages::internalError(asyncResp->res);
1555 return;
1556 }
1557 asyncResp->res.jsonValue["@odata.type"] =
1558 "#LogEntry.v1_8_0.LogEntry";
1559 asyncResp->res.jsonValue["@odata.id"] =
1560 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1561 std::to_string(*id);
1562 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1563 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1564 asyncResp->res.jsonValue["Message"] = *message;
1565 asyncResp->res.jsonValue["Resolved"] = resolved;
1566 asyncResp->res.jsonValue["EntryType"] = "Event";
1567 asyncResp->res.jsonValue["Severity"] =
1568 translateSeverityDbusToRedfish(*severity);
1569 asyncResp->res.jsonValue["Created"] =
1570 crow::utility::getDateTimeUintMs(*timestamp);
1571 asyncResp->res.jsonValue["Modified"] =
1572 crow::utility::getDateTimeUintMs(*updateTimestamp);
1573 if (filePath != nullptr)
1574 {
1575 asyncResp->res.jsonValue["AdditionalDataURI"] =
1576 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1577 std::to_string(*id) + "/attachment";
1578 }
1579 },
1580 "xyz.openbmc_project.Logging",
1581 "/xyz/openbmc_project/logging/entry/" + entryID,
1582 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001583 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001584
1585 BMCWEB_ROUTE(
1586 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001587 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001588 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001589 [&app](const crow::Request& req,
1590 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1591 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001592 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001593 {
1594 return;
1595 }
1596 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001597
Ed Tanous002d39b2022-05-31 08:59:27 -07001598 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1599 resolved))
1600 {
1601 return;
1602 }
1603 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001604
Ed Tanous002d39b2022-05-31 08:59:27 -07001605 crow::connections::systemBus->async_method_call(
1606 [asyncResp, entryId](const boost::system::error_code ec) {
1607 if (ec)
1608 {
1609 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1610 messages::internalError(asyncResp->res);
1611 return;
1612 }
1613 },
1614 "xyz.openbmc_project.Logging",
1615 "/xyz/openbmc_project/logging/entry/" + entryId,
1616 "org.freedesktop.DBus.Properties", "Set",
1617 "xyz.openbmc_project.Logging.Entry", "Resolved",
1618 dbus::utility::DbusVariantType(*resolved));
1619 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001620
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001621 BMCWEB_ROUTE(
1622 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001623 .privileges(redfish::privileges::deleteLogEntry)
1624
Ed Tanous002d39b2022-05-31 08:59:27 -07001625 .methods(boost::beast::http::verb::delete_)(
1626 [&app](const crow::Request& req,
1627 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1628 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001629 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001630 {
1631 return;
1632 }
1633 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1634
1635 std::string entryID = param;
1636
1637 dbus::utility::escapePathForDbus(entryID);
1638
1639 // Process response from Logging service.
1640 auto respHandler =
1641 [asyncResp, entryID](const boost::system::error_code ec) {
1642 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1643 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001644 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001645 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001646 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001647 messages::resourceNotFound(asyncResp->res, "LogEntry",
1648 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001649 return;
1650 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001651 // TODO Handle for specific error code
1652 BMCWEB_LOG_ERROR
1653 << "EventLogEntry (DBus) doDelete respHandler got error "
1654 << ec;
1655 asyncResp->res.result(
1656 boost::beast::http::status::internal_server_error);
1657 return;
1658 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001659
Ed Tanous002d39b2022-05-31 08:59:27 -07001660 asyncResp->res.result(boost::beast::http::status::ok);
1661 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001662
Ed Tanous002d39b2022-05-31 08:59:27 -07001663 // Make call to Logging service to request Delete Log
1664 crow::connections::systemBus->async_method_call(
1665 respHandler, "xyz.openbmc_project.Logging",
1666 "/xyz/openbmc_project/logging/entry/" + entryID,
1667 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001668 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001669}
1670
1671inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001672{
George Liu0fda0f12021-11-16 10:06:17 +08001673 BMCWEB_ROUTE(
1674 app,
1675 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001676 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001677 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001678 [&app](const crow::Request& req,
1679 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1680 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001681 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001682 {
1683 return;
1684 }
1685 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1686 {
1687 asyncResp->res.result(boost::beast::http::status::bad_request);
1688 return;
1689 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001690
Ed Tanous002d39b2022-05-31 08:59:27 -07001691 std::string entryID = param;
1692 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001693
Ed Tanous002d39b2022-05-31 08:59:27 -07001694 crow::connections::systemBus->async_method_call(
1695 [asyncResp, entryID](const boost::system::error_code ec,
1696 const sdbusplus::message::unix_fd& unixfd) {
1697 if (ec.value() == EBADR)
1698 {
1699 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1700 entryID);
1701 return;
1702 }
1703 if (ec)
1704 {
1705 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1706 messages::internalError(asyncResp->res);
1707 return;
1708 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001709
Ed Tanous002d39b2022-05-31 08:59:27 -07001710 int fd = -1;
1711 fd = dup(unixfd);
1712 if (fd == -1)
1713 {
1714 messages::internalError(asyncResp->res);
1715 return;
1716 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001717
Ed Tanous002d39b2022-05-31 08:59:27 -07001718 long long int size = lseek(fd, 0, SEEK_END);
1719 if (size == -1)
1720 {
1721 messages::internalError(asyncResp->res);
1722 return;
1723 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001724
Ed Tanous002d39b2022-05-31 08:59:27 -07001725 // Arbitrary max size of 64kb
1726 constexpr int maxFileSize = 65536;
1727 if (size > maxFileSize)
1728 {
1729 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1730 << maxFileSize;
1731 messages::internalError(asyncResp->res);
1732 return;
1733 }
1734 std::vector<char> data(static_cast<size_t>(size));
1735 long long int rc = lseek(fd, 0, SEEK_SET);
1736 if (rc == -1)
1737 {
1738 messages::internalError(asyncResp->res);
1739 return;
1740 }
1741 rc = read(fd, data.data(), data.size());
1742 if ((rc == -1) || (rc != size))
1743 {
1744 messages::internalError(asyncResp->res);
1745 return;
1746 }
1747 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001748
Ed Tanous002d39b2022-05-31 08:59:27 -07001749 std::string_view strData(data.data(), data.size());
1750 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001751
Ed Tanous002d39b2022-05-31 08:59:27 -07001752 asyncResp->res.addHeader("Content-Type",
1753 "application/octet-stream");
1754 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
1755 asyncResp->res.body() = std::move(output);
1756 },
1757 "xyz.openbmc_project.Logging",
1758 "/xyz/openbmc_project/logging/entry/" + entryID,
1759 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1760 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001761}
1762
Spencer Kub7028eb2021-10-26 15:27:35 +08001763constexpr const char* hostLoggerFolderPath = "/var/log/console";
1764
1765inline bool
1766 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1767 std::vector<std::filesystem::path>& hostLoggerFiles)
1768{
1769 std::error_code ec;
1770 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1771 if (ec)
1772 {
1773 BMCWEB_LOG_ERROR << ec.message();
1774 return false;
1775 }
1776 for (const std::filesystem::directory_entry& it : logPath)
1777 {
1778 std::string filename = it.path().filename();
1779 // Prefix of each log files is "log". Find the file and save the
1780 // path
Ed Tanous11ba3972022-07-11 09:50:41 -07001781 if (filename.starts_with("log"))
Spencer Kub7028eb2021-10-26 15:27:35 +08001782 {
1783 hostLoggerFiles.emplace_back(it.path());
1784 }
1785 }
1786 // As the log files rotate, they are appended with a ".#" that is higher for
1787 // the older logs. Since we start from oldest logs, sort the name in
1788 // descending order.
1789 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1790 AlphanumLess<std::string>());
1791
1792 return true;
1793}
1794
Ed Tanous02cad962022-06-30 16:50:15 -07001795inline bool getHostLoggerEntries(
1796 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1797 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001798{
1799 GzFileReader logFile;
1800
1801 // Go though all log files and expose host logs.
1802 for (const std::filesystem::path& it : hostLoggerFiles)
1803 {
1804 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1805 {
1806 BMCWEB_LOG_ERROR << "fail to expose host logs";
1807 return false;
1808 }
1809 }
1810 // Get lastMessage from constructor by getter
1811 std::string lastMessage = logFile.getLastMessage();
1812 if (!lastMessage.empty())
1813 {
1814 logCount++;
1815 if (logCount > skip && logCount <= (skip + top))
1816 {
1817 logEntries.push_back(lastMessage);
1818 }
1819 }
1820 return true;
1821}
1822
1823inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1824 const std::string& msg,
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001825 nlohmann::json::object_t& logEntryJson)
Spencer Kub7028eb2021-10-26 15:27:35 +08001826{
1827 // Fill in the log entry with the gathered data.
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001828 logEntryJson["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
1829 logEntryJson["@odata.id"] =
1830 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1831 logEntryID;
1832 logEntryJson["Name"] = "Host Logger Entry";
1833 logEntryJson["Id"] = logEntryID;
1834 logEntryJson["Message"] = msg;
1835 logEntryJson["EntryType"] = "Oem";
1836 logEntryJson["Severity"] = "OK";
1837 logEntryJson["OemRecordFormat"] = "Host Logger Entry";
Spencer Kub7028eb2021-10-26 15:27:35 +08001838}
1839
1840inline void requestRoutesSystemHostLogger(App& app)
1841{
1842 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1843 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001844 .methods(boost::beast::http::verb::get)(
1845 [&app](const crow::Request& req,
1846 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001847 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001848 {
1849 return;
1850 }
1851 asyncResp->res.jsonValue["@odata.id"] =
1852 "/redfish/v1/Systems/system/LogServices/HostLogger";
1853 asyncResp->res.jsonValue["@odata.type"] =
1854 "#LogService.v1_1_0.LogService";
1855 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1856 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1857 asyncResp->res.jsonValue["Id"] = "HostLogger";
1858 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1859 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1860 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001861}
1862
1863inline void requestRoutesSystemHostLoggerCollection(App& app)
1864{
1865 BMCWEB_ROUTE(app,
1866 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1867 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001868 .methods(boost::beast::http::verb::get)(
1869 [&app](const crow::Request& req,
1870 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1871 query_param::QueryCapabilities capabilities = {
1872 .canDelegateTop = true,
1873 .canDelegateSkip = true,
1874 };
1875 query_param::Query delegatedQuery;
1876 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001877 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001878 {
1879 return;
1880 }
1881 asyncResp->res.jsonValue["@odata.id"] =
1882 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1883 asyncResp->res.jsonValue["@odata.type"] =
1884 "#LogEntryCollection.LogEntryCollection";
1885 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1886 asyncResp->res.jsonValue["Description"] =
1887 "Collection of HostLogger Entries";
1888 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1889 logEntryArray = nlohmann::json::array();
1890 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001891
Ed Tanous002d39b2022-05-31 08:59:27 -07001892 std::vector<std::filesystem::path> hostLoggerFiles;
1893 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1894 {
1895 BMCWEB_LOG_ERROR << "fail to get host log file path";
1896 return;
1897 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001898 // If we weren't provided top and skip limits, use the defaults.
1899 size_t skip = delegatedQuery.skip.value_or(0);
1900 size_t top =
1901 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
Ed Tanous002d39b2022-05-31 08:59:27 -07001902 size_t logCount = 0;
1903 // This vector only store the entries we want to expose that
1904 // control by skip and top.
1905 std::vector<std::string> logEntries;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001906 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1907 logCount))
Ed Tanous002d39b2022-05-31 08:59:27 -07001908 {
1909 messages::internalError(asyncResp->res);
1910 return;
1911 }
1912 // If vector is empty, that means skip value larger than total
1913 // log count
1914 if (logEntries.empty())
1915 {
1916 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1917 return;
1918 }
1919 if (!logEntries.empty())
1920 {
1921 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001922 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001923 nlohmann::json::object_t hostLogEntry;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001924 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
1925 hostLogEntry);
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001926 logEntryArray.push_back(std::move(hostLogEntry));
George Liu0fda0f12021-11-16 10:06:17 +08001927 }
1928
Ed Tanous002d39b2022-05-31 08:59:27 -07001929 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001930 if (skip + top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001931 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001932 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1933 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001934 std::to_string(skip + top);
George Liu0fda0f12021-11-16 10:06:17 +08001935 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001936 }
George Liu0fda0f12021-11-16 10:06:17 +08001937 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001938}
1939
1940inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1941{
1942 BMCWEB_ROUTE(
1943 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1944 .privileges(redfish::privileges::getLogEntry)
1945 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001946 [&app](const crow::Request& req,
1947 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1948 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001949 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001950 {
1951 return;
1952 }
1953 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001954
Ed Tanous002d39b2022-05-31 08:59:27 -07001955 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001956
Ed Tanous002d39b2022-05-31 08:59:27 -07001957 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1958 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001959
Ed Tanous002d39b2022-05-31 08:59:27 -07001960 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1961 if (ec == std::errc::invalid_argument)
1962 {
1963 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1964 return;
1965 }
1966 if (ec == std::errc::result_out_of_range)
1967 {
1968 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1969 return;
1970 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001971
Ed Tanous002d39b2022-05-31 08:59:27 -07001972 std::vector<std::filesystem::path> hostLoggerFiles;
1973 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1974 {
1975 BMCWEB_LOG_ERROR << "fail to get host log file path";
1976 return;
1977 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001978
Ed Tanous002d39b2022-05-31 08:59:27 -07001979 size_t logCount = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001980 size_t top = 1;
Ed Tanous002d39b2022-05-31 08:59:27 -07001981 std::vector<std::string> logEntries;
1982 // We can get specific entry by skip and top. For example, if we
1983 // want to get nth entry, we can set skip = n-1 and top = 1 to
1984 // get that entry
1985 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1986 logCount))
1987 {
1988 messages::internalError(asyncResp->res);
1989 return;
1990 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001991
Ed Tanous002d39b2022-05-31 08:59:27 -07001992 if (!logEntries.empty())
1993 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001994 nlohmann::json::object_t hostLogEntry;
1995 fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
1996 asyncResp->res.jsonValue.update(hostLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07001997 return;
1998 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001999
Ed Tanous002d39b2022-05-31 08:59:27 -07002000 // Requested ID was not found
2001 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2002 });
Spencer Kub7028eb2021-10-26 15:27:35 +08002003}
2004
Claire Weinanfdd26902022-03-01 14:18:25 -08002005constexpr char const* dumpManagerIface =
2006 "xyz.openbmc_project.Collection.DeleteAll";
2007inline void handleLogServicesCollectionGet(
2008 crow::App& app, const crow::Request& req,
2009 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2010{
2011 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2012 {
2013 return;
2014 }
2015 // Collections don't include the static data added by SubRoute
2016 // because it has a duplicate entry for members
2017 asyncResp->res.jsonValue["@odata.type"] =
2018 "#LogServiceCollection.LogServiceCollection";
2019 asyncResp->res.jsonValue["@odata.id"] =
2020 "/redfish/v1/Managers/bmc/LogServices";
2021 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2022 asyncResp->res.jsonValue["Description"] =
2023 "Collection of LogServices for this Manager";
2024 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2025 logServiceArray = nlohmann::json::array();
2026
2027#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
2028 logServiceArray.push_back(
2029 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
2030#endif
2031
2032 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2033
2034#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2035 auto respHandler =
2036 [asyncResp](
2037 const boost::system::error_code ec,
2038 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2039 if (ec)
2040 {
2041 BMCWEB_LOG_ERROR
2042 << "handleLogServicesCollectionGet respHandler got error "
2043 << ec;
2044 // Assume that getting an error simply means there are no dump
2045 // LogServices. Return without adding any error response.
2046 return;
2047 }
2048
2049 nlohmann::json& logServiceArrayLocal =
2050 asyncResp->res.jsonValue["Members"];
2051
2052 for (const std::string& path : subTreePaths)
2053 {
2054 if (path == "/xyz/openbmc_project/dump/bmc")
2055 {
2056 logServiceArrayLocal.push_back(
2057 {{"@odata.id",
2058 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
2059 }
2060 else if (path == "/xyz/openbmc_project/dump/faultlog")
2061 {
2062 logServiceArrayLocal.push_back(
2063 {{"@odata.id",
2064 "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
2065 }
2066 }
2067
2068 asyncResp->res.jsonValue["Members@odata.count"] =
2069 logServiceArrayLocal.size();
2070 };
2071
2072 crow::connections::systemBus->async_method_call(
2073 respHandler, "xyz.openbmc_project.ObjectMapper",
2074 "/xyz/openbmc_project/object_mapper",
2075 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2076 "/xyz/openbmc_project/dump", 0,
2077 std::array<const char*, 1>{dumpManagerIface});
2078#endif
2079}
2080
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002081inline void requestRoutesBMCLogServiceCollection(App& app)
2082{
2083 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002084 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002085 .methods(boost::beast::http::verb::get)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002086 std::bind_front(handleLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002087}
Ed Tanous1da66f72018-07-27 16:13:37 -07002088
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002089inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002090{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002091 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002092 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002093 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002094 [&app](const crow::Request& req,
2095 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002096 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002097 {
2098 return;
2099 }
2100 asyncResp->res.jsonValue["@odata.type"] =
2101 "#LogService.v1_1_0.LogService";
2102 asyncResp->res.jsonValue["@odata.id"] =
2103 "/redfish/v1/Managers/bmc/LogServices/Journal";
2104 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2105 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2106 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2107 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302108
Ed Tanous002d39b2022-05-31 08:59:27 -07002109 std::pair<std::string, std::string> redfishDateTimeOffset =
2110 crow::utility::getDateTimeOffsetNow();
2111 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2112 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2113 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302114
Ed Tanous002d39b2022-05-31 08:59:27 -07002115 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2116 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2117 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002118}
Jason M. Billse1f26342018-07-18 12:12:00 -07002119
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002120static int
2121 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2122 sd_journal* journal,
2123 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002124{
2125 // Get the Log Entry contents
2126 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002127
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002128 std::string message;
2129 std::string_view syslogID;
2130 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2131 if (ret < 0)
2132 {
2133 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2134 << strerror(-ret);
2135 }
2136 if (!syslogID.empty())
2137 {
2138 message += std::string(syslogID) + ": ";
2139 }
2140
Ed Tanous39e77502019-03-04 17:35:53 -08002141 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002142 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002143 if (ret < 0)
2144 {
2145 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2146 return 1;
2147 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002148 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002149
2150 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002151 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002152 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002153 if (ret < 0)
2154 {
2155 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002156 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002157
2158 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002159 std::string entryTimeStr;
2160 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002161 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002162 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002163 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002164
2165 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07002166 bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
2167 bmcJournalLogEntryJson["@odata.id"] =
2168 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2169 bmcJournalLogEntryID;
2170 bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2171 bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2172 bmcJournalLogEntryJson["Message"] = std::move(message);
2173 bmcJournalLogEntryJson["EntryType"] = "Oem";
2174 bmcJournalLogEntryJson["Severity"] = severity <= 2 ? "Critical"
2175 : severity <= 4 ? "Warning"
2176 : "OK";
2177 bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2178 bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
Jason M. Billse1f26342018-07-18 12:12:00 -07002179 return 0;
2180}
2181
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002182inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002183{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002184 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002185 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002186 .methods(boost::beast::http::verb::get)(
2187 [&app](const crow::Request& req,
2188 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2189 query_param::QueryCapabilities capabilities = {
2190 .canDelegateTop = true,
2191 .canDelegateSkip = true,
2192 };
2193 query_param::Query delegatedQuery;
2194 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002195 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002196 {
2197 return;
2198 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07002199
2200 size_t skip = delegatedQuery.skip.value_or(0);
2201 size_t top =
2202 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
2203
Ed Tanous002d39b2022-05-31 08:59:27 -07002204 // Collections don't include the static data added by SubRoute
2205 // because it has a duplicate entry for members
2206 asyncResp->res.jsonValue["@odata.type"] =
2207 "#LogEntryCollection.LogEntryCollection";
2208 asyncResp->res.jsonValue["@odata.id"] =
2209 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2210 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2211 asyncResp->res.jsonValue["Description"] =
2212 "Collection of BMC Journal Entries";
2213 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2214 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002215
Ed Tanous002d39b2022-05-31 08:59:27 -07002216 // Go through the journal and use the timestamp to create a
2217 // unique ID for each entry
2218 sd_journal* journalTmp = nullptr;
2219 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2220 if (ret < 0)
2221 {
2222 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2223 messages::internalError(asyncResp->res);
2224 return;
2225 }
2226 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2227 journalTmp, sd_journal_close);
2228 journalTmp = nullptr;
2229 uint64_t entryCount = 0;
2230 // Reset the unique ID on the first entry
2231 bool firstEntry = true;
2232 SD_JOURNAL_FOREACH(journal.get())
2233 {
2234 entryCount++;
2235 // Handle paging using skip (number of entries to skip from
2236 // the start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07002237 if (entryCount <= skip || entryCount > skip + top)
George Liu0fda0f12021-11-16 10:06:17 +08002238 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002239 continue;
2240 }
2241
2242 std::string idStr;
2243 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2244 {
2245 continue;
2246 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002247 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002248
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002249 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002250 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2251 bmcJournalLogEntry) != 0)
2252 {
George Liu0fda0f12021-11-16 10:06:17 +08002253 messages::internalError(asyncResp->res);
2254 return;
2255 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002256 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002257 }
2258 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07002259 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07002260 {
2261 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2262 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07002263 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07002264 }
George Liu0fda0f12021-11-16 10:06:17 +08002265 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002266}
Jason M. Billse1f26342018-07-18 12:12:00 -07002267
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002268inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002269{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002270 BMCWEB_ROUTE(app,
2271 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002272 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002273 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002274 [&app](const crow::Request& req,
2275 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2276 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002277 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002278 {
2279 return;
2280 }
2281 // Convert the unique ID back to a timestamp to find the entry
2282 uint64_t ts = 0;
2283 uint64_t index = 0;
2284 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2285 {
2286 return;
2287 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002288
Ed Tanous002d39b2022-05-31 08:59:27 -07002289 sd_journal* journalTmp = nullptr;
2290 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2291 if (ret < 0)
2292 {
2293 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2294 messages::internalError(asyncResp->res);
2295 return;
2296 }
2297 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2298 journalTmp, sd_journal_close);
2299 journalTmp = nullptr;
2300 // Go to the timestamp in the log and move to the entry at the
2301 // index tracking the unique ID
2302 std::string idStr;
2303 bool firstEntry = true;
2304 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2305 if (ret < 0)
2306 {
2307 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2308 << strerror(-ret);
2309 messages::internalError(asyncResp->res);
2310 return;
2311 }
2312 for (uint64_t i = 0; i <= index; i++)
2313 {
2314 sd_journal_next(journal.get());
2315 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2316 {
2317 messages::internalError(asyncResp->res);
2318 return;
2319 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002320 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002321 }
2322 // Confirm that the entry ID matches what was requested
2323 if (idStr != entryID)
2324 {
2325 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2326 return;
2327 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002328
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002329 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002330 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002331 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002332 {
2333 messages::internalError(asyncResp->res);
2334 return;
2335 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07002336 asyncResp->res.jsonValue.update(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002337 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002338}
2339
Claire Weinanfdd26902022-03-01 14:18:25 -08002340inline void
2341 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2342 const std::string& dumpType)
2343{
2344 std::string dumpPath;
2345 std::string overWritePolicy;
2346 bool collectDiagnosticDataSupported = false;
2347
2348 if (dumpType == "BMC")
2349 {
2350 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2351 overWritePolicy = "WrapsWhenFull";
2352 collectDiagnosticDataSupported = true;
2353 }
2354 else if (dumpType == "FaultLog")
2355 {
2356 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2357 overWritePolicy = "Unknown";
2358 collectDiagnosticDataSupported = false;
2359 }
2360 else if (dumpType == "System")
2361 {
2362 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2363 overWritePolicy = "WrapsWhenFull";
2364 collectDiagnosticDataSupported = true;
2365 }
2366 else
2367 {
2368 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2369 << dumpType;
2370 messages::internalError(asyncResp->res);
2371 return;
2372 }
2373
2374 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2375 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2376 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2377 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2378 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2379 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2380
2381 std::pair<std::string, std::string> redfishDateTimeOffset =
2382 crow::utility::getDateTimeOffsetNow();
2383 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2384 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2385 redfishDateTimeOffset.second;
2386
2387 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2388 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2389 dumpPath + "/Actions/LogService.ClearLog";
2390
2391 if (collectDiagnosticDataSupported)
2392 {
2393 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2394 ["target"] =
2395 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2396 }
2397}
2398
2399inline void handleLogServicesDumpServiceGet(
2400 crow::App& app, const std::string& dumpType, const crow::Request& req,
2401 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2402{
2403 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2404 {
2405 return;
2406 }
2407 getDumpServiceInfo(asyncResp, dumpType);
2408}
2409
2410inline void handleLogServicesDumpEntriesCollectionGet(
2411 crow::App& app, const std::string& dumpType, const crow::Request& req,
2412 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2413{
2414 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2415 {
2416 return;
2417 }
2418 getDumpEntryCollection(asyncResp, dumpType);
2419}
2420
2421inline void handleLogServicesDumpEntryGet(
2422 crow::App& app, const std::string& dumpType, const crow::Request& req,
2423 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2424 const std::string& dumpId)
2425{
2426 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2427 {
2428 return;
2429 }
2430 getDumpEntryById(asyncResp, dumpId, dumpType);
2431}
2432
2433inline void handleLogServicesDumpEntryDelete(
2434 crow::App& app, const std::string& dumpType, const crow::Request& req,
2435 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2436 const std::string& dumpId)
2437{
2438 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2439 {
2440 return;
2441 }
2442 deleteDumpEntry(asyncResp, dumpId, dumpType);
2443}
2444
2445inline void handleLogServicesDumpCollectDiagnosticDataPost(
2446 crow::App& app, const std::string& dumpType, const crow::Request& req,
2447 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2448{
2449 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2450 {
2451 return;
2452 }
2453 createDump(asyncResp, req, dumpType);
2454}
2455
2456inline void handleLogServicesDumpClearLogPost(
2457 crow::App& app, const std::string& dumpType, const crow::Request& req,
2458 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2459{
2460 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2461 {
2462 return;
2463 }
2464 clearDump(asyncResp, dumpType);
2465}
2466
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002467inline void requestRoutesBMCDumpService(App& app)
2468{
2469 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002470 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002471 .methods(boost::beast::http::verb::get)(std::bind_front(
2472 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002473}
2474
2475inline void requestRoutesBMCDumpEntryCollection(App& app)
2476{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002477 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002478 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002479 .methods(boost::beast::http::verb::get)(std::bind_front(
2480 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002481}
2482
2483inline void requestRoutesBMCDumpEntry(App& app)
2484{
2485 BMCWEB_ROUTE(app,
2486 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002487 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002488 .methods(boost::beast::http::verb::get)(std::bind_front(
2489 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2490
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002491 BMCWEB_ROUTE(app,
2492 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002493 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002494 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2495 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002496}
2497
2498inline void requestRoutesBMCDumpCreate(App& app)
2499{
George Liu0fda0f12021-11-16 10:06:17 +08002500 BMCWEB_ROUTE(
2501 app,
2502 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002503 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002504 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002505 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2506 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002507}
2508
2509inline void requestRoutesBMCDumpClear(App& app)
2510{
George Liu0fda0f12021-11-16 10:06:17 +08002511 BMCWEB_ROUTE(
2512 app,
2513 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002514 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002515 .methods(boost::beast::http::verb::post)(std::bind_front(
2516 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2517}
2518
2519inline void requestRoutesFaultLogDumpService(App& app)
2520{
2521 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2522 .privileges(redfish::privileges::getLogService)
2523 .methods(boost::beast::http::verb::get)(std::bind_front(
2524 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2525}
2526
2527inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2528{
2529 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2530 .privileges(redfish::privileges::getLogEntryCollection)
2531 .methods(boost::beast::http::verb::get)(
2532 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2533 std::ref(app), "FaultLog"));
2534}
2535
2536inline void requestRoutesFaultLogDumpEntry(App& app)
2537{
2538 BMCWEB_ROUTE(app,
2539 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2540 .privileges(redfish::privileges::getLogEntry)
2541 .methods(boost::beast::http::verb::get)(std::bind_front(
2542 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2543
2544 BMCWEB_ROUTE(app,
2545 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2546 .privileges(redfish::privileges::deleteLogEntry)
2547 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2548 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2549}
2550
2551inline void requestRoutesFaultLogDumpClear(App& app)
2552{
2553 BMCWEB_ROUTE(
2554 app,
2555 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2556 .privileges(redfish::privileges::postLogService)
2557 .methods(boost::beast::http::verb::post)(std::bind_front(
2558 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002559}
2560
2561inline void requestRoutesSystemDumpService(App& app)
2562{
2563 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002564 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002565 .methods(boost::beast::http::verb::get)(
2566 [&app](const crow::Request& req,
2567 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002568 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002569 {
2570 return;
2571 }
2572 asyncResp->res.jsonValue["@odata.id"] =
2573 "/redfish/v1/Systems/system/LogServices/Dump";
2574 asyncResp->res.jsonValue["@odata.type"] =
2575 "#LogService.v1_2_0.LogService";
2576 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2577 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2578 asyncResp->res.jsonValue["Id"] = "Dump";
2579 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302580
Ed Tanous002d39b2022-05-31 08:59:27 -07002581 std::pair<std::string, std::string> redfishDateTimeOffset =
2582 crow::utility::getDateTimeOffsetNow();
2583 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2584 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2585 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302586
Ed Tanous002d39b2022-05-31 08:59:27 -07002587 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2588 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2589 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2590 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002591
Ed Tanous002d39b2022-05-31 08:59:27 -07002592 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2593 ["target"] =
2594 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002595 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002596}
2597
2598inline void requestRoutesSystemDumpEntryCollection(App& app)
2599{
2600
2601 /**
2602 * Functions triggers appropriate requests on DBus
2603 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002604 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002605 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002606 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002607 [&app](const crow::Request& req,
2608 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002609 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002610 {
2611 return;
2612 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002613 getDumpEntryCollection(asyncResp, "System");
2614 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002615}
2616
2617inline void requestRoutesSystemDumpEntry(App& app)
2618{
2619 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002620 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002621 .privileges(redfish::privileges::getLogEntry)
2622
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002623 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002624 [&app](const crow::Request& req,
2625 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2626 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002627 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002628 {
2629 return;
2630 }
2631 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002632 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002633
2634 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002635 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002636 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002637 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002638 [&app](const crow::Request& req,
2639 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2640 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002641 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002642 {
2643 return;
2644 }
2645 deleteDumpEntry(asyncResp, param, "system");
2646 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002647}
2648
2649inline void requestRoutesSystemDumpCreate(App& app)
2650{
George Liu0fda0f12021-11-16 10:06:17 +08002651 BMCWEB_ROUTE(
2652 app,
2653 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002654 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002655 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002656 [&app](const crow::Request& req,
2657 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002658 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002659 {
2660 return;
2661 }
2662 createDump(asyncResp, req, "System");
2663 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002664}
2665
2666inline void requestRoutesSystemDumpClear(App& app)
2667{
George Liu0fda0f12021-11-16 10:06:17 +08002668 BMCWEB_ROUTE(
2669 app,
2670 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002671 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002672 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002673 [&app](const crow::Request& req,
2674 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002675
Ed Tanous45ca1b82022-03-25 13:07:27 -07002676 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002677 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002678 {
2679 return;
2680 }
2681 clearDump(asyncResp, "System");
2682 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002683}
2684
2685inline void requestRoutesCrashdumpService(App& app)
2686{
2687 // Note: Deviated from redfish privilege registry for GET & HEAD
2688 // method for security reasons.
2689 /**
2690 * Functions triggers appropriate requests on DBus
2691 */
2692 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002693 // This is incorrect, should be:
2694 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002695 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002696 .methods(boost::beast::http::verb::get)(
2697 [&app](const crow::Request& req,
2698 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002699 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002700 {
2701 return;
2702 }
2703 // Copy over the static data to include the entries added by
2704 // SubRoute
2705 asyncResp->res.jsonValue["@odata.id"] =
2706 "/redfish/v1/Systems/system/LogServices/Crashdump";
2707 asyncResp->res.jsonValue["@odata.type"] =
2708 "#LogService.v1_2_0.LogService";
2709 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2710 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2711 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2712 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2713 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302714
Ed Tanous002d39b2022-05-31 08:59:27 -07002715 std::pair<std::string, std::string> redfishDateTimeOffset =
2716 crow::utility::getDateTimeOffsetNow();
2717 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2718 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2719 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302720
Ed Tanous002d39b2022-05-31 08:59:27 -07002721 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2722 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2723 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2724 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2725 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2726 ["target"] =
2727 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002728 });
2729}
2730
2731void inline requestRoutesCrashdumpClear(App& app)
2732{
George Liu0fda0f12021-11-16 10:06:17 +08002733 BMCWEB_ROUTE(
2734 app,
2735 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002736 // This is incorrect, should be:
2737 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002738 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002739 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002740 [&app](const crow::Request& req,
2741 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002742 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002743 {
2744 return;
2745 }
2746 crow::connections::systemBus->async_method_call(
2747 [asyncResp](const boost::system::error_code ec,
2748 const std::string&) {
2749 if (ec)
2750 {
2751 messages::internalError(asyncResp->res);
2752 return;
2753 }
2754 messages::success(asyncResp->res);
2755 },
2756 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2757 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002758}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002759
zhanghch058d1b46d2021-04-01 11:18:24 +08002760static void
2761 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2762 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002763{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002764 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002765 [asyncResp, logID,
2766 &logEntryJson](const boost::system::error_code ec,
2767 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002768 if (ec)
2769 {
2770 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2771 if (ec.value() ==
2772 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002773 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002774 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002775 }
2776 else
2777 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002778 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002779 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002780 return;
2781 }
2782
2783 std::string timestamp{};
2784 std::string filename{};
2785 std::string logfile{};
2786 parseCrashdumpParameters(params, filename, timestamp, logfile);
2787
2788 if (filename.empty() || timestamp.empty())
2789 {
2790 messages::resourceMissingAtURI(asyncResp->res,
2791 crow::utility::urlFromPieces(logID));
2792 return;
2793 }
2794
2795 std::string crashdumpURI =
2796 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2797 logID + "/" + filename;
Jason M. Bills84afc482022-06-24 12:38:23 -07002798 nlohmann::json::object_t logEntry;
2799 logEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
2800 logEntry["@odata.id"] =
2801 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
2802 logEntry["Name"] = "CPU Crashdump";
2803 logEntry["Id"] = logID;
2804 logEntry["EntryType"] = "Oem";
2805 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2806 logEntry["DiagnosticDataType"] = "OEM";
2807 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2808 logEntry["Created"] = std::move(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07002809
2810 // If logEntryJson references an array of LogEntry resources
2811 // ('Members' list), then push this as a new entry, otherwise set it
2812 // directly
2813 if (logEntryJson.is_array())
2814 {
2815 logEntryJson.push_back(logEntry);
2816 asyncResp->res.jsonValue["Members@odata.count"] =
2817 logEntryJson.size();
2818 }
2819 else
2820 {
Jason M. Billsd405bb52022-06-24 10:52:05 -07002821 logEntryJson.update(logEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002822 }
2823 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002824 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002825 std::move(getStoredLogCallback), crashdumpObject,
2826 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002827 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002828}
2829
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002830inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002831{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002832 // Note: Deviated from redfish privilege registry for GET & HEAD
2833 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002834 /**
2835 * Functions triggers appropriate requests on DBus
2836 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002837 BMCWEB_ROUTE(app,
2838 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002839 // This is incorrect, should be.
2840 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002841 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002842 .methods(boost::beast::http::verb::get)(
2843 [&app](const crow::Request& req,
2844 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002845 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002846 {
2847 return;
2848 }
2849 crow::connections::systemBus->async_method_call(
2850 [asyncResp](const boost::system::error_code ec,
2851 const std::vector<std::string>& resp) {
2852 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002853 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002854 if (ec.value() !=
2855 boost::system::errc::no_such_file_or_directory)
2856 {
2857 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2858 << ec.message();
2859 messages::internalError(asyncResp->res);
2860 return;
2861 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002862 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002863 asyncResp->res.jsonValue["@odata.type"] =
2864 "#LogEntryCollection.LogEntryCollection";
2865 asyncResp->res.jsonValue["@odata.id"] =
2866 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2867 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2868 asyncResp->res.jsonValue["Description"] =
2869 "Collection of Crashdump Entries";
2870 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2871 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002872
Ed Tanous002d39b2022-05-31 08:59:27 -07002873 for (const std::string& path : resp)
2874 {
2875 const sdbusplus::message::object_path objPath(path);
2876 // Get the log ID
2877 std::string logID = objPath.filename();
2878 if (logID.empty())
2879 {
2880 continue;
2881 }
2882 // Add the log entry to the array
2883 logCrashdumpEntry(asyncResp, logID,
2884 asyncResp->res.jsonValue["Members"]);
2885 }
2886 },
2887 "xyz.openbmc_project.ObjectMapper",
2888 "/xyz/openbmc_project/object_mapper",
2889 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2890 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002891 });
2892}
Ed Tanous1da66f72018-07-27 16:13:37 -07002893
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002894inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002895{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002896 // Note: Deviated from redfish privilege registry for GET & HEAD
2897 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002898
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002899 BMCWEB_ROUTE(
2900 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002901 // this is incorrect, should be
2902 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002903 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002904 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002905 [&app](const crow::Request& req,
2906 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2907 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002908 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002909 {
2910 return;
2911 }
2912 const std::string& logID = param;
2913 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2914 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002915}
Ed Tanous1da66f72018-07-27 16:13:37 -07002916
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002917inline void requestRoutesCrashdumpFile(App& app)
2918{
2919 // Note: Deviated from redfish privilege registry for GET & HEAD
2920 // method for security reasons.
2921 BMCWEB_ROUTE(
2922 app,
2923 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002924 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002925 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002926 [&app](const crow::Request& req,
2927 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2928 const std::string& logID, const std::string& fileName) {
Shounak Mitra2a9beee2022-07-20 18:41:30 +00002929 // Do not call getRedfishRoute here since the crashdump file is not a
2930 // Redfish resource.
Ed Tanous002d39b2022-05-31 08:59:27 -07002931 auto getStoredLogCallback =
2932 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2933 const boost::system::error_code ec,
2934 const std::vector<
2935 std::pair<std::string, dbus::utility::DbusVariantType>>&
2936 resp) {
2937 if (ec)
2938 {
2939 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2940 messages::internalError(asyncResp->res);
2941 return;
2942 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002943
Ed Tanous002d39b2022-05-31 08:59:27 -07002944 std::string dbusFilename{};
2945 std::string dbusTimestamp{};
2946 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002947
Ed Tanous002d39b2022-05-31 08:59:27 -07002948 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2949 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002950
Ed Tanous002d39b2022-05-31 08:59:27 -07002951 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2952 dbusFilepath.empty())
2953 {
2954 messages::resourceMissingAtURI(asyncResp->res, url);
2955 return;
2956 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002957
Ed Tanous002d39b2022-05-31 08:59:27 -07002958 // Verify the file name parameter is correct
2959 if (fileName != dbusFilename)
2960 {
2961 messages::resourceMissingAtURI(asyncResp->res, url);
2962 return;
2963 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002964
Ed Tanous002d39b2022-05-31 08:59:27 -07002965 if (!std::filesystem::exists(dbusFilepath))
2966 {
2967 messages::resourceMissingAtURI(asyncResp->res, url);
2968 return;
2969 }
2970 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2971 asyncResp->res.body() =
2972 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002973
Ed Tanous002d39b2022-05-31 08:59:27 -07002974 // Configure this to be a file download when accessed
2975 // from a browser
2976 asyncResp->res.addHeader("Content-Disposition", "attachment");
2977 };
2978 crow::connections::systemBus->async_method_call(
2979 std::move(getStoredLogCallback), crashdumpObject,
2980 crashdumpPath + std::string("/") + logID,
2981 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2982 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002983}
2984
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002985enum class OEMDiagnosticType
2986{
2987 onDemand,
2988 telemetry,
2989 invalid,
2990};
2991
Ed Tanousf7725d72022-03-07 12:46:00 -08002992inline OEMDiagnosticType
2993 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002994{
2995 if (oemDiagStr == "OnDemand")
2996 {
2997 return OEMDiagnosticType::onDemand;
2998 }
2999 if (oemDiagStr == "Telemetry")
3000 {
3001 return OEMDiagnosticType::telemetry;
3002 }
3003
3004 return OEMDiagnosticType::invalid;
3005}
3006
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003007inline void requestRoutesCrashdumpCollect(App& app)
3008{
3009 // Note: Deviated from redfish privilege registry for GET & HEAD
3010 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08003011 BMCWEB_ROUTE(
3012 app,
3013 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07003014 // The below is incorrect; Should be ConfigureManager
3015 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003016 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07003017 .methods(boost::beast::http::verb::post)(
3018 [&app](const crow::Request& req,
3019 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003020 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003021 {
3022 return;
3023 }
3024 std::string diagnosticDataType;
3025 std::string oemDiagnosticDataType;
3026 if (!redfish::json_util::readJsonAction(
3027 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3028 "OEMDiagnosticDataType", oemDiagnosticDataType))
3029 {
3030 return;
3031 }
3032
3033 if (diagnosticDataType != "OEM")
3034 {
3035 BMCWEB_LOG_ERROR
3036 << "Only OEM DiagnosticDataType supported for Crashdump";
3037 messages::actionParameterValueFormatError(
3038 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3039 "CollectDiagnosticData");
3040 return;
3041 }
3042
3043 OEMDiagnosticType oemDiagType =
3044 getOEMDiagnosticType(oemDiagnosticDataType);
3045
3046 std::string iface;
3047 std::string method;
3048 std::string taskMatchStr;
3049 if (oemDiagType == OEMDiagnosticType::onDemand)
3050 {
3051 iface = crashdumpOnDemandInterface;
3052 method = "GenerateOnDemandLog";
3053 taskMatchStr = "type='signal',"
3054 "interface='org.freedesktop.DBus.Properties',"
3055 "member='PropertiesChanged',"
3056 "arg0namespace='com.intel.crashdump'";
3057 }
3058 else if (oemDiagType == OEMDiagnosticType::telemetry)
3059 {
3060 iface = crashdumpTelemetryInterface;
3061 method = "GenerateTelemetryLog";
3062 taskMatchStr = "type='signal',"
3063 "interface='org.freedesktop.DBus.Properties',"
3064 "member='PropertiesChanged',"
3065 "arg0namespace='com.intel.crashdump'";
3066 }
3067 else
3068 {
3069 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3070 << oemDiagnosticDataType;
3071 messages::actionParameterValueFormatError(
3072 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3073 "CollectDiagnosticData");
3074 return;
3075 }
3076
3077 auto collectCrashdumpCallback =
3078 [asyncResp, payload(task::Payload(req)),
3079 taskMatchStr](const boost::system::error_code ec,
3080 const std::string&) mutable {
3081 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003082 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003083 if (ec.value() == boost::system::errc::operation_not_supported)
3084 {
3085 messages::resourceInStandby(asyncResp->res);
3086 }
3087 else if (ec.value() ==
3088 boost::system::errc::device_or_resource_busy)
3089 {
3090 messages::serviceTemporarilyUnavailable(asyncResp->res,
3091 "60");
3092 }
3093 else
3094 {
3095 messages::internalError(asyncResp->res);
3096 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003097 return;
3098 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003099 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -05003100 [](boost::system::error_code err, sdbusplus::message_t&,
Ed Tanous002d39b2022-05-31 08:59:27 -07003101 const std::shared_ptr<task::TaskData>& taskData) {
3102 if (!err)
3103 {
3104 taskData->messages.emplace_back(messages::taskCompletedOK(
3105 std::to_string(taskData->index)));
3106 taskData->state = "Completed";
3107 }
3108 return task::completed;
3109 },
3110 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003111
Ed Tanous002d39b2022-05-31 08:59:27 -07003112 task->startTimer(std::chrono::minutes(5));
3113 task->populateResp(asyncResp->res);
3114 task->payload.emplace(std::move(payload));
3115 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003116
Ed Tanous002d39b2022-05-31 08:59:27 -07003117 crow::connections::systemBus->async_method_call(
3118 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3119 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003120 });
3121}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003122
Andrew Geisslercb92c032018-08-17 07:56:14 -07003123/**
3124 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3125 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003126inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003127{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003128 /**
3129 * Function handles POST method request.
3130 * The Clear Log actions does not require any parameter.The action deletes
3131 * all entries found in the Entries collection for this Log Service.
3132 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003133
George Liu0fda0f12021-11-16 10:06:17 +08003134 BMCWEB_ROUTE(
3135 app,
3136 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003137 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003138 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003139 [&app](const crow::Request& req,
3140 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003141 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003142 {
3143 return;
3144 }
3145 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003146
Ed Tanous002d39b2022-05-31 08:59:27 -07003147 // Process response from Logging service.
3148 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3149 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3150 if (ec)
3151 {
3152 // TODO Handle for specific error code
3153 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3154 asyncResp->res.result(
3155 boost::beast::http::status::internal_server_error);
3156 return;
3157 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003158
Ed Tanous002d39b2022-05-31 08:59:27 -07003159 asyncResp->res.result(boost::beast::http::status::no_content);
3160 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003161
Ed Tanous002d39b2022-05-31 08:59:27 -07003162 // Make call to Logging service to request Clear Log
3163 crow::connections::systemBus->async_method_call(
3164 respHandler, "xyz.openbmc_project.Logging",
3165 "/xyz/openbmc_project/logging",
3166 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3167 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003168}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003169
3170/****************************************************
3171 * Redfish PostCode interfaces
3172 * using DBUS interface: getPostCodesTS
3173 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003174inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003175{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003176 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003177 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003178 .methods(boost::beast::http::verb::get)(
3179 [&app](const crow::Request& req,
3180 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003181 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003182 {
3183 return;
3184 }
Ed Tanous14766872022-03-15 10:44:42 -07003185
Ed Tanous002d39b2022-05-31 08:59:27 -07003186 asyncResp->res.jsonValue["@odata.id"] =
3187 "/redfish/v1/Systems/system/LogServices/PostCodes";
3188 asyncResp->res.jsonValue["@odata.type"] =
3189 "#LogService.v1_1_0.LogService";
3190 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3191 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3192 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3193 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3194 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3195 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303196
Ed Tanous002d39b2022-05-31 08:59:27 -07003197 std::pair<std::string, std::string> redfishDateTimeOffset =
3198 crow::utility::getDateTimeOffsetNow();
3199 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3200 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3201 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303202
Ed Tanous002d39b2022-05-31 08:59:27 -07003203 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3204 {"target",
3205 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003206 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003207}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003208
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003209inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003210{
George Liu0fda0f12021-11-16 10:06:17 +08003211 BMCWEB_ROUTE(
3212 app,
3213 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003214 // The following privilege is incorrect; It should be ConfigureManager
3215 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003216 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003217 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003218 [&app](const crow::Request& req,
3219 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003220 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003221 {
3222 return;
3223 }
3224 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003225
Ed Tanous002d39b2022-05-31 08:59:27 -07003226 // Make call to post-code service to request clear all
3227 crow::connections::systemBus->async_method_call(
3228 [asyncResp](const boost::system::error_code ec) {
3229 if (ec)
3230 {
3231 // TODO Handle for specific error code
3232 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3233 << ec;
3234 asyncResp->res.result(
3235 boost::beast::http::status::internal_server_error);
3236 messages::internalError(asyncResp->res);
3237 return;
3238 }
3239 },
3240 "xyz.openbmc_project.State.Boot.PostCode0",
3241 "/xyz/openbmc_project/State/Boot/PostCode0",
3242 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3243 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003244}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003245
3246static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003247 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303248 const boost::container::flat_map<
3249 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003250 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3251 const uint64_t skip = 0, const uint64_t top = 0)
3252{
3253 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003254 const registries::Message* message =
3255 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003256
3257 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003258 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003259
3260 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303261 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3262 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003263 {
3264 currentCodeIndex++;
3265 std::string postcodeEntryID =
3266 "B" + std::to_string(bootIndex) + "-" +
3267 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3268
3269 uint64_t usecSinceEpoch = code.first;
3270 uint64_t usTimeOffset = 0;
3271
3272 if (1 == currentCodeIndex)
3273 { // already incremented
3274 firstCodeTimeUs = code.first;
3275 }
3276 else
3277 {
3278 usTimeOffset = code.first - firstCodeTimeUs;
3279 }
3280
3281 // skip if no specific codeIndex is specified and currentCodeIndex does
3282 // not fall between top and skip
3283 if ((codeIndex == 0) &&
3284 (currentCodeIndex <= skip || currentCodeIndex > top))
3285 {
3286 continue;
3287 }
3288
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003289 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003290 // currentIndex
3291 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3292 {
3293 // This is done for simplicity. 1st entry is needed to calculate
3294 // time offset. To improve efficiency, one can get to the entry
3295 // directly (possibly with flatmap's nth method)
3296 continue;
3297 }
3298
3299 // currentCodeIndex is within top and skip or equal to specified code
3300 // index
3301
3302 // Get the Created time from the timestamp
3303 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003304 entryTimeStr =
3305 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003306
3307 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3308 std::ostringstream hexCode;
3309 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303310 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003311 std::ostringstream timeOffsetStr;
3312 // Set Fixed -Point Notation
3313 timeOffsetStr << std::fixed;
3314 // Set precision to 4 digits
3315 timeOffsetStr << std::setprecision(4);
3316 // Add double to stream
3317 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3318 std::vector<std::string> messageArgs = {
3319 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3320
3321 // Get MessageArgs template from message registry
3322 std::string msg;
3323 if (message != nullptr)
3324 {
3325 msg = message->message;
3326
3327 // fill in this post code value
3328 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003329 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003330 {
3331 std::string argStr = "%" + std::to_string(++i);
3332 size_t argPos = msg.find(argStr);
3333 if (argPos != std::string::npos)
3334 {
3335 msg.replace(argPos, argStr.length(), messageArg);
3336 }
3337 }
3338 }
3339
Tim Leed4342a92020-04-27 11:47:58 +08003340 // Get Severity template from message registry
3341 std::string severity;
3342 if (message != nullptr)
3343 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003344 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003345 }
3346
ZhikuiRena3316fc2020-01-29 14:58:08 -08003347 // add to AsyncResp
3348 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003349 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills84afc482022-06-24 12:38:23 -07003350 bmcLogEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
3351 bmcLogEntry["@odata.id"] =
3352 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3353 postcodeEntryID;
3354 bmcLogEntry["Name"] = "POST Code Log Entry";
3355 bmcLogEntry["Id"] = postcodeEntryID;
3356 bmcLogEntry["Message"] = std::move(msg);
3357 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3358 bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3359 bmcLogEntry["EntryType"] = "Event";
3360 bmcLogEntry["Severity"] = std::move(severity);
3361 bmcLogEntry["Created"] = entryTimeStr;
George Liu647b3cd2021-07-05 12:43:56 +08003362 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3363 {
3364 bmcLogEntry["AdditionalDataURI"] =
3365 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3366 postcodeEntryID + "/attachment";
3367 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003368 }
3369}
3370
zhanghch058d1b46d2021-04-01 11:18:24 +08003371static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003372 const uint16_t bootIndex,
3373 const uint64_t codeIndex)
3374{
3375 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303376 [aResp, bootIndex,
3377 codeIndex](const boost::system::error_code ec,
3378 const boost::container::flat_map<
3379 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3380 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003381 if (ec)
3382 {
3383 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3384 messages::internalError(aResp->res);
3385 return;
3386 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003387
Ed Tanous002d39b2022-05-31 08:59:27 -07003388 // skip the empty postcode boots
3389 if (postcode.empty())
3390 {
3391 return;
3392 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003393
Ed Tanous002d39b2022-05-31 08:59:27 -07003394 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003395
Ed Tanous002d39b2022-05-31 08:59:27 -07003396 aResp->res.jsonValue["Members@odata.count"] =
3397 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003398 },
Jonathan Doman15124762021-01-07 17:54:17 -08003399 "xyz.openbmc_project.State.Boot.PostCode0",
3400 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003401 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3402 bootIndex);
3403}
3404
zhanghch058d1b46d2021-04-01 11:18:24 +08003405static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003406 const uint16_t bootIndex,
3407 const uint16_t bootCount,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003408 const uint64_t entryCount, size_t skip,
3409 size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003410{
3411 crow::connections::systemBus->async_method_call(
3412 [aResp, bootIndex, bootCount, entryCount, skip,
3413 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303414 const boost::container::flat_map<
3415 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3416 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003417 if (ec)
3418 {
3419 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3420 messages::internalError(aResp->res);
3421 return;
3422 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003423
Ed Tanous002d39b2022-05-31 08:59:27 -07003424 uint64_t endCount = entryCount;
3425 if (!postcode.empty())
3426 {
3427 endCount = entryCount + postcode.size();
Ed Tanous3648c8b2022-07-25 13:39:59 -07003428 if (skip < endCount && (top + skip) > entryCount)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003429 {
Ed Tanous3648c8b2022-07-25 13:39:59 -07003430 uint64_t thisBootSkip =
3431 std::max(static_cast<uint64_t>(skip), entryCount) -
3432 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003433 uint64_t thisBootTop =
Ed Tanous3648c8b2022-07-25 13:39:59 -07003434 std::min(static_cast<uint64_t>(top + skip), endCount) -
3435 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003436
3437 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3438 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003439 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003440 aResp->res.jsonValue["Members@odata.count"] = endCount;
3441 }
3442
3443 // continue to previous bootIndex
3444 if (bootIndex < bootCount)
3445 {
3446 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3447 bootCount, endCount, skip, top);
3448 }
3449 else
3450 {
3451 aResp->res.jsonValue["Members@odata.nextLink"] =
3452 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3453 std::to_string(skip + top);
3454 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003455 },
Jonathan Doman15124762021-01-07 17:54:17 -08003456 "xyz.openbmc_project.State.Boot.PostCode0",
3457 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003458 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3459 bootIndex);
3460}
3461
zhanghch058d1b46d2021-04-01 11:18:24 +08003462static void
3463 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003464 size_t skip, size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003465{
3466 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003467 sdbusplus::asio::getProperty<uint16_t>(
3468 *crow::connections::systemBus,
3469 "xyz.openbmc_project.State.Boot.PostCode0",
3470 "/xyz/openbmc_project/State/Boot/PostCode0",
3471 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3472 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3473 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003474 if (ec)
3475 {
3476 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3477 messages::internalError(aResp->res);
3478 return;
3479 }
3480 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003481 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003482}
3483
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003484inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003485{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003486 BMCWEB_ROUTE(app,
3487 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003488 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003489 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003490 [&app](const crow::Request& req,
3491 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003492 query_param::QueryCapabilities capabilities = {
3493 .canDelegateTop = true,
3494 .canDelegateSkip = true,
3495 };
3496 query_param::Query delegatedQuery;
3497 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003498 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003499 {
3500 return;
3501 }
3502 asyncResp->res.jsonValue["@odata.type"] =
3503 "#LogEntryCollection.LogEntryCollection";
3504 asyncResp->res.jsonValue["@odata.id"] =
3505 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3506 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3507 asyncResp->res.jsonValue["Description"] =
3508 "Collection of POST Code Log Entries";
3509 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3510 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07003511 size_t skip = delegatedQuery.skip.value_or(0);
3512 size_t top =
3513 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
3514 getCurrentBootNumber(asyncResp, skip, top);
Ed Tanous002d39b2022-05-31 08:59:27 -07003515 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003516}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003517
George Liu647b3cd2021-07-05 12:43:56 +08003518/**
3519 * @brief Parse post code ID and get the current value and index value
3520 * eg: postCodeID=B1-2, currentValue=1, index=2
3521 *
3522 * @param[in] postCodeID Post Code ID
3523 * @param[out] currentValue Current value
3524 * @param[out] index Index value
3525 *
3526 * @return bool true if the parsing is successful, false the parsing fails
3527 */
3528inline static bool parsePostCode(const std::string& postCodeID,
3529 uint64_t& currentValue, uint16_t& index)
3530{
3531 std::vector<std::string> split;
3532 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3533 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3534 {
3535 return false;
3536 }
3537
Ed Tanousca45aa32022-01-07 09:28:45 -08003538 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003539 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003540 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003541 const char* end = split[0].data() + split[0].size();
3542 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3543
3544 if (ptrIndex != end || ecIndex != std::errc())
3545 {
3546 return false;
3547 }
3548
3549 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003550
3551 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003552 end = split[1].data() + split[1].size();
3553 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003554
Tony Lee517d9a52022-06-28 15:41:23 +08003555 return ptrValue == end && ecValue == std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003556}
3557
3558inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3559{
George Liu0fda0f12021-11-16 10:06:17 +08003560 BMCWEB_ROUTE(
3561 app,
3562 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003563 .privileges(redfish::privileges::getLogEntry)
3564 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003565 [&app](const crow::Request& req,
3566 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3567 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003568 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003569 {
3570 return;
3571 }
3572 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3573 {
3574 asyncResp->res.result(boost::beast::http::status::bad_request);
3575 return;
3576 }
George Liu647b3cd2021-07-05 12:43:56 +08003577
Ed Tanous002d39b2022-05-31 08:59:27 -07003578 uint64_t currentValue = 0;
3579 uint16_t index = 0;
3580 if (!parsePostCode(postCodeID, currentValue, index))
3581 {
3582 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3583 return;
3584 }
George Liu647b3cd2021-07-05 12:43:56 +08003585
Ed Tanous002d39b2022-05-31 08:59:27 -07003586 crow::connections::systemBus->async_method_call(
3587 [asyncResp, postCodeID, currentValue](
3588 const boost::system::error_code ec,
3589 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3590 postcodes) {
3591 if (ec.value() == EBADR)
3592 {
3593 messages::resourceNotFound(asyncResp->res, "LogEntry",
3594 postCodeID);
3595 return;
3596 }
3597 if (ec)
3598 {
3599 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3600 messages::internalError(asyncResp->res);
3601 return;
3602 }
George Liu647b3cd2021-07-05 12:43:56 +08003603
Ed Tanous002d39b2022-05-31 08:59:27 -07003604 size_t value = static_cast<size_t>(currentValue) - 1;
3605 if (value == std::string::npos || postcodes.size() < currentValue)
3606 {
3607 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3608 messages::resourceNotFound(asyncResp->res, "LogEntry",
3609 postCodeID);
3610 return;
3611 }
George Liu647b3cd2021-07-05 12:43:56 +08003612
Ed Tanous002d39b2022-05-31 08:59:27 -07003613 const auto& [tID, c] = postcodes[value];
3614 if (c.empty())
3615 {
3616 BMCWEB_LOG_INFO << "No found post code data";
3617 messages::resourceNotFound(asyncResp->res, "LogEntry",
3618 postCodeID);
3619 return;
3620 }
3621 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3622 const char* d = reinterpret_cast<const char*>(c.data());
3623 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003624
Ed Tanous002d39b2022-05-31 08:59:27 -07003625 asyncResp->res.addHeader("Content-Type",
3626 "application/octet-stream");
3627 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
3628 asyncResp->res.body() = crow::utility::base64encode(strData);
3629 },
3630 "xyz.openbmc_project.State.Boot.PostCode0",
3631 "/xyz/openbmc_project/State/Boot/PostCode0",
3632 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3633 });
George Liu647b3cd2021-07-05 12:43:56 +08003634}
3635
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003636inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003637{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003638 BMCWEB_ROUTE(
3639 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003640 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003641 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003642 [&app](const crow::Request& req,
3643 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3644 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003645 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003646 {
3647 return;
3648 }
3649 uint16_t bootIndex = 0;
3650 uint64_t codeIndex = 0;
3651 if (!parsePostCode(targetID, codeIndex, bootIndex))
3652 {
3653 // Requested ID was not found
3654 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3655 return;
3656 }
3657 if (bootIndex == 0 || codeIndex == 0)
3658 {
3659 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3660 << targetID;
3661 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003662
Ed Tanous002d39b2022-05-31 08:59:27 -07003663 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3664 asyncResp->res.jsonValue["@odata.id"] =
3665 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3666 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3667 asyncResp->res.jsonValue["Description"] =
3668 "Collection of POST Code Log Entries";
3669 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3670 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003671
Ed Tanous002d39b2022-05-31 08:59:27 -07003672 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3673 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003674}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003675
Ed Tanous1da66f72018-07-27 16:13:37 -07003676} // namespace redfish