blob: a04dfb55b8fe50264aee680a22687e146e3fda07 [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
Claire Weinanaefe3782022-07-15 19:17:19 -0700329inline void parseDumpEntryFromDbusObject(
330 const dbus::utility::ManagedItem& object, std::string& dumpStatus,
331 uint64_t& size, uint64_t& timestamp,
332 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
333{
334 for (const auto& interfaceMap : object.second)
335 {
336 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
337 {
338 for (const auto& propertyMap : interfaceMap.second)
339 {
340 if (propertyMap.first == "Status")
341 {
342 const auto* status =
343 std::get_if<std::string>(&propertyMap.second);
344 if (status == nullptr)
345 {
346 messages::internalError(asyncResp->res);
347 break;
348 }
349 dumpStatus = *status;
350 }
351 }
352 }
353 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
354 {
355 for (const auto& propertyMap : interfaceMap.second)
356 {
357 if (propertyMap.first == "Size")
358 {
359 const auto* sizePtr =
360 std::get_if<uint64_t>(&propertyMap.second);
361 if (sizePtr == nullptr)
362 {
363 messages::internalError(asyncResp->res);
364 break;
365 }
366 size = *sizePtr;
367 break;
368 }
369 }
370 }
371 else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime")
372 {
373 for (const auto& propertyMap : interfaceMap.second)
374 {
375 if (propertyMap.first == "Elapsed")
376 {
377 const uint64_t* usecsTimeStamp =
378 std::get_if<uint64_t>(&propertyMap.second);
379 if (usecsTimeStamp == nullptr)
380 {
381 messages::internalError(asyncResp->res);
382 break;
383 }
384 timestamp = *usecsTimeStamp;
385 break;
386 }
387 }
388 }
389 }
390}
391
Nan Zhou21ab4042022-06-26 23:07:40 +0000392static std::string getDumpEntriesPath(const std::string& dumpType)
Claire Weinanfdd26902022-03-01 14:18:25 -0800393{
394 std::string entriesPath;
395
396 if (dumpType == "BMC")
397 {
398 entriesPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
399 }
400 else if (dumpType == "FaultLog")
401 {
402 entriesPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/";
403 }
404 else if (dumpType == "System")
405 {
406 entriesPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
407 }
408 else
409 {
410 BMCWEB_LOG_ERROR << "getDumpEntriesPath() invalid dump type: "
411 << dumpType;
412 }
413
414 // Returns empty string on error
415 return entriesPath;
416}
417
zhanghch058d1b46d2021-04-01 11:18:24 +0800418inline void
419 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
420 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500421{
Claire Weinanfdd26902022-03-01 14:18:25 -0800422 std::string entriesPath = getDumpEntriesPath(dumpType);
423 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500424 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500425 messages::internalError(asyncResp->res);
426 return;
427 }
428
429 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800430 [asyncResp, entriesPath,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800431 dumpType](const boost::system::error_code ec,
432 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700433 if (ec)
434 {
435 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
436 messages::internalError(asyncResp->res);
437 return;
438 }
439
Claire Weinanfdd26902022-03-01 14:18:25 -0800440 // Remove ending slash
441 std::string odataIdStr = entriesPath;
442 if (!odataIdStr.empty())
443 {
444 odataIdStr.pop_back();
445 }
446
447 asyncResp->res.jsonValue["@odata.type"] =
448 "#LogEntryCollection.LogEntryCollection";
449 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
450 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
451 asyncResp->res.jsonValue["Description"] =
452 "Collection of " + dumpType + " Dump Entries";
453
Ed Tanous002d39b2022-05-31 08:59:27 -0700454 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
455 entriesArray = nlohmann::json::array();
456 std::string dumpEntryPath =
457 "/xyz/openbmc_project/dump/" +
458 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
459
460 std::sort(resp.begin(), resp.end(), [](const auto& l, const auto& r) {
461 return AlphanumLess<std::string>()(l.first.filename(),
462 r.first.filename());
463 });
464
465 for (auto& object : resp)
466 {
467 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500468 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700469 continue;
470 }
471 uint64_t timestamp = 0;
472 uint64_t size = 0;
473 std::string dumpStatus;
Jason M. Bills433b68b2022-06-28 12:24:26 -0700474 nlohmann::json::object_t thisEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -0700475
476 std::string entryID = object.first.filename();
477 if (entryID.empty())
478 {
479 continue;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500480 }
481
Claire Weinanaefe3782022-07-15 19:17:19 -0700482 parseDumpEntryFromDbusObject(object, dumpStatus, size, timestamp,
483 asyncResp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700484
485 if (dumpStatus !=
486 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
487 !dumpStatus.empty())
488 {
489 // Dump status is not Complete, no need to enumerate
490 continue;
491 }
492
493 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800494 thisEntry["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700495 thisEntry["Id"] = entryID;
496 thisEntry["EntryType"] = "Event";
497 thisEntry["Created"] = crow::utility::getDateTimeUint(timestamp);
498 thisEntry["Name"] = dumpType + " Dump Entry";
499
Ed Tanous002d39b2022-05-31 08:59:27 -0700500 if (dumpType == "BMC")
501 {
502 thisEntry["DiagnosticDataType"] = "Manager";
503 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800504 entriesPath + entryID + "/attachment";
505 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700506 }
507 else if (dumpType == "System")
508 {
509 thisEntry["DiagnosticDataType"] = "OEM";
510 thisEntry["OEMDiagnosticDataType"] = "System";
511 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800512 entriesPath + entryID + "/attachment";
513 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700514 }
515 entriesArray.push_back(std::move(thisEntry));
516 }
517 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500518 },
519 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
520 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
521}
522
zhanghch058d1b46d2021-04-01 11:18:24 +0800523inline void
Claire Weinanc7a6d662022-06-13 16:36:39 -0700524 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800525 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500526{
Claire Weinanfdd26902022-03-01 14:18:25 -0800527 std::string entriesPath = getDumpEntriesPath(dumpType);
528 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500529 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500530 messages::internalError(asyncResp->res);
531 return;
532 }
533
534 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800535 [asyncResp, entryID, dumpType,
536 entriesPath](const boost::system::error_code ec,
Ed Tanous02cad962022-06-30 16:50:15 -0700537 const dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700538 if (ec)
539 {
540 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
541 messages::internalError(asyncResp->res);
542 return;
543 }
544
545 bool foundDumpEntry = false;
546 std::string dumpEntryPath =
547 "/xyz/openbmc_project/dump/" +
548 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
549
550 for (const auto& objectPath : resp)
551 {
552 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500553 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700554 continue;
555 }
556
557 foundDumpEntry = true;
558 uint64_t timestamp = 0;
559 uint64_t size = 0;
560 std::string dumpStatus;
561
Claire Weinanaefe3782022-07-15 19:17:19 -0700562 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size,
563 timestamp, asyncResp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700564
565 if (dumpStatus !=
566 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
567 !dumpStatus.empty())
568 {
569 // Dump status is not Complete
570 // return not found until status is changed to Completed
571 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
572 entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500573 return;
574 }
575
Ed Tanous002d39b2022-05-31 08:59:27 -0700576 asyncResp->res.jsonValue["@odata.type"] =
577 "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800578 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700579 asyncResp->res.jsonValue["Id"] = entryID;
580 asyncResp->res.jsonValue["EntryType"] = "Event";
581 asyncResp->res.jsonValue["Created"] =
582 crow::utility::getDateTimeUint(timestamp);
583 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500584
Ed Tanous002d39b2022-05-31 08:59:27 -0700585 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500586 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700587 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
588 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800589 entriesPath + entryID + "/attachment";
590 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500591 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700592 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500593 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700594 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
595 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
596 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800597 entriesPath + entryID + "/attachment";
598 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500599 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700600 }
601 if (!foundDumpEntry)
602 {
603 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
604 messages::internalError(asyncResp->res);
605 return;
606 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500607 },
608 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
609 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
610}
611
zhanghch058d1b46d2021-04-01 11:18:24 +0800612inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800613 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500614 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500615{
Ed Tanous002d39b2022-05-31 08:59:27 -0700616 auto respHandler =
617 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500618 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
619 if (ec)
620 {
George Liu3de8d8b2021-03-22 17:49:39 +0800621 if (ec.value() == EBADR)
622 {
623 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
624 return;
625 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500626 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -0800627 << ec << " entryID=" << entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500628 messages::internalError(asyncResp->res);
629 return;
630 }
631 };
632 crow::connections::systemBus->async_method_call(
633 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500634 "/xyz/openbmc_project/dump/" +
635 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
636 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500637 "xyz.openbmc_project.Object.Delete", "Delete");
638}
639
zhanghch058d1b46d2021-04-01 11:18:24 +0800640inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700641 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800642 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
643 const uint32_t& dumpId, const std::string& dumpPath,
644 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500645{
646 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -0500647 [dumpId, dumpPath,
648 dumpType](boost::system::error_code err, sdbusplus::message_t& m,
649 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700650 if (err)
651 {
652 BMCWEB_LOG_ERROR << "Error in creating a dump";
653 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500654 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700655 }
656
657 dbus::utility::DBusInteracesMap interfacesList;
658
659 sdbusplus::message::object_path objPath;
660
661 m.read(objPath, interfacesList);
662
663 if (objPath.str ==
664 "/xyz/openbmc_project/dump/" +
665 std::string(boost::algorithm::to_lower_copy(dumpType)) +
666 "/entry/" + std::to_string(dumpId))
667 {
668 nlohmann::json retMessage = messages::success();
669 taskData->messages.emplace_back(retMessage);
670
671 std::string headerLoc =
672 "Location: " + dumpPath + std::to_string(dumpId);
673 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
674
675 taskData->state = "Completed";
676 return task::completed;
677 }
678 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500679 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800680 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500681 "member='InterfacesAdded', "
682 "path='/xyz/openbmc_project/dump'");
683
684 task->startTimer(std::chrono::minutes(3));
685 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700686 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500687}
688
zhanghch058d1b46d2021-04-01 11:18:24 +0800689inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
690 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500691{
Claire Weinanfdd26902022-03-01 14:18:25 -0800692 std::string dumpPath = getDumpEntriesPath(dumpType);
693 if (dumpPath.empty())
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500694 {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500695 messages::internalError(asyncResp->res);
696 return;
697 }
698
699 std::optional<std::string> diagnosticDataType;
700 std::optional<std::string> oemDiagnosticDataType;
701
Willy Tu15ed6782021-12-14 11:03:16 -0800702 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500703 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
704 "OEMDiagnosticDataType", oemDiagnosticDataType))
705 {
706 return;
707 }
708
709 if (dumpType == "System")
710 {
711 if (!oemDiagnosticDataType || !diagnosticDataType)
712 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800713 BMCWEB_LOG_ERROR
714 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500715 messages::actionParameterMissing(
716 asyncResp->res, "CollectDiagnosticData",
717 "DiagnosticDataType & OEMDiagnosticDataType");
718 return;
719 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700720 if ((*oemDiagnosticDataType != "System") ||
721 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500722 {
723 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700724 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500725 return;
726 }
727 }
728 else if (dumpType == "BMC")
729 {
730 if (!diagnosticDataType)
731 {
George Liu0fda0f12021-11-16 10:06:17 +0800732 BMCWEB_LOG_ERROR
733 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500734 messages::actionParameterMissing(
735 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
736 return;
737 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700738 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500739 {
740 BMCWEB_LOG_ERROR
741 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700742 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500743 return;
744 }
745 }
746
747 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700748 [asyncResp, payload(task::Payload(req)), dumpPath,
749 dumpType](const boost::system::error_code ec,
750 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700751 if (ec)
752 {
753 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
754 messages::internalError(asyncResp->res);
755 return;
756 }
757 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500758
Ed Tanous002d39b2022-05-31 08:59:27 -0700759 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
760 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500761 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500762 "xyz.openbmc_project.Dump.Manager",
763 "/xyz/openbmc_project/dump/" +
764 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500765 "xyz.openbmc_project.Dump.Create", "CreateDump");
766}
767
zhanghch058d1b46d2021-04-01 11:18:24 +0800768inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
769 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500770{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500771 std::string dumpTypeLowerCopy =
772 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800773
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500774 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800775 [asyncResp, dumpType](
776 const boost::system::error_code ec,
777 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700778 if (ec)
779 {
780 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
781 messages::internalError(asyncResp->res);
782 return;
783 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500784
Ed Tanous002d39b2022-05-31 08:59:27 -0700785 for (const std::string& path : subTreePaths)
786 {
787 sdbusplus::message::object_path objPath(path);
788 std::string logID = objPath.filename();
789 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500790 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700791 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500792 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700793 deleteDumpEntry(asyncResp, logID, dumpType);
794 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500795 },
796 "xyz.openbmc_project.ObjectMapper",
797 "/xyz/openbmc_project/object_mapper",
798 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500799 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
800 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
801 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500802}
803
Ed Tanousb9d36b42022-02-26 21:42:46 -0800804inline static void
805 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
806 std::string& filename, std::string& timestamp,
807 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700808{
809 for (auto property : params)
810 {
811 if (property.first == "Timestamp")
812 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500813 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500814 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700815 if (value != nullptr)
816 {
817 timestamp = *value;
818 }
819 }
820 else if (property.first == "Filename")
821 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500822 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500823 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700824 if (value != nullptr)
825 {
826 filename = *value;
827 }
828 }
829 else if (property.first == "Log")
830 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500831 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500832 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700833 if (value != nullptr)
834 {
835 logfile = *value;
836 }
837 }
838 }
839}
840
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500841constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700842inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700843{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800844 /**
845 * Functions triggers appropriate requests on DBus
846 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700847 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700848 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700849 .methods(boost::beast::http::verb::get)(
850 [&app](const crow::Request& req,
851 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000852 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700853 {
854 return;
855 }
856 // Collections don't include the static data added by SubRoute
857 // because it has a duplicate entry for members
858 asyncResp->res.jsonValue["@odata.type"] =
859 "#LogServiceCollection.LogServiceCollection";
860 asyncResp->res.jsonValue["@odata.id"] =
861 "/redfish/v1/Systems/system/LogServices";
862 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
863 asyncResp->res.jsonValue["Description"] =
864 "Collection of LogServices for this Computer System";
865 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
866 logServiceArray = nlohmann::json::array();
867 nlohmann::json::object_t eventLog;
868 eventLog["@odata.id"] =
869 "/redfish/v1/Systems/system/LogServices/EventLog";
870 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500871#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700872 nlohmann::json::object_t dumpLog;
873 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
874 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600875#endif
876
Jason M. Billsd53dd412019-02-12 17:16:22 -0800877#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700878 nlohmann::json::object_t crashdump;
879 crashdump["@odata.id"] =
880 "/redfish/v1/Systems/system/LogServices/Crashdump";
881 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800882#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800883
884#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700885 nlohmann::json::object_t hostlogger;
886 hostlogger["@odata.id"] =
887 "/redfish/v1/Systems/system/LogServices/HostLogger";
888 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800889#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700890 asyncResp->res.jsonValue["Members@odata.count"] =
891 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800892
Ed Tanous002d39b2022-05-31 08:59:27 -0700893 crow::connections::systemBus->async_method_call(
894 [asyncResp](const boost::system::error_code ec,
895 const dbus::utility::MapperGetSubTreePathsResponse&
896 subtreePath) {
897 if (ec)
898 {
899 BMCWEB_LOG_ERROR << ec;
900 return;
901 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700902
Ed Tanous002d39b2022-05-31 08:59:27 -0700903 for (const auto& pathStr : subtreePath)
904 {
905 if (pathStr.find("PostCode") != std::string::npos)
906 {
907 nlohmann::json& logServiceArrayLocal =
908 asyncResp->res.jsonValue["Members"];
909 logServiceArrayLocal.push_back(
910 {{"@odata.id",
911 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
912 asyncResp->res.jsonValue["Members@odata.count"] =
913 logServiceArrayLocal.size();
914 return;
915 }
916 }
917 },
918 "xyz.openbmc_project.ObjectMapper",
919 "/xyz/openbmc_project/object_mapper",
920 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
921 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700922 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700923}
924
925inline void requestRoutesEventLogService(App& app)
926{
927 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700928 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700929 .methods(boost::beast::http::verb::get)(
930 [&app](const crow::Request& req,
931 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000932 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700933 {
934 return;
935 }
936 asyncResp->res.jsonValue["@odata.id"] =
937 "/redfish/v1/Systems/system/LogServices/EventLog";
938 asyncResp->res.jsonValue["@odata.type"] =
939 "#LogService.v1_1_0.LogService";
940 asyncResp->res.jsonValue["Name"] = "Event Log Service";
941 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
942 asyncResp->res.jsonValue["Id"] = "EventLog";
943 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530944
Ed Tanous002d39b2022-05-31 08:59:27 -0700945 std::pair<std::string, std::string> redfishDateTimeOffset =
946 crow::utility::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530947
Ed Tanous002d39b2022-05-31 08:59:27 -0700948 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
949 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
950 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +0530951
Ed Tanous002d39b2022-05-31 08:59:27 -0700952 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
953 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
954 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700955
Ed Tanous002d39b2022-05-31 08:59:27 -0700956 {"target",
957 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700958 });
959}
960
961inline void requestRoutesJournalEventLogClear(App& app)
962{
Jason M. Bills4978b632022-02-22 14:17:43 -0800963 BMCWEB_ROUTE(
964 app,
965 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -0700966 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700967 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700968 [&app](const crow::Request& req,
969 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000970 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700971 {
972 return;
973 }
974 // Clear the EventLog by deleting the log files
975 std::vector<std::filesystem::path> redfishLogFiles;
976 if (getRedfishLogFiles(redfishLogFiles))
977 {
978 for (const std::filesystem::path& file : redfishLogFiles)
979 {
980 std::error_code ec;
981 std::filesystem::remove(file, ec);
982 }
983 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800984
Ed Tanous002d39b2022-05-31 08:59:27 -0700985 // Reload rsyslog so it knows to start new log files
986 crow::connections::systemBus->async_method_call(
987 [asyncResp](const boost::system::error_code ec) {
988 if (ec)
989 {
990 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
991 messages::internalError(asyncResp->res);
992 return;
993 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800994
Ed Tanous002d39b2022-05-31 08:59:27 -0700995 messages::success(asyncResp->res);
996 },
997 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
998 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
999 "replace");
1000 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001001}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001002
Jason M. Billsac992cd2022-06-24 13:31:46 -07001003enum class LogParseError
1004{
1005 success,
1006 parseFailed,
1007 messageIdNotInRegistry,
1008};
1009
1010static LogParseError
1011 fillEventLogEntryJson(const std::string& logEntryID,
1012 const std::string& logEntry,
1013 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001014{
Jason M. Bills95820182019-04-22 16:25:34 -07001015 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001016 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001017 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001018 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001019 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001020 return LogParseError::parseFailed;
Jason M. Bills95820182019-04-22 16:25:34 -07001021 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001022 std::string timestamp = logEntry.substr(0, space);
1023 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001024 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001025 if (entryStart == std::string::npos)
1026 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001027 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001028 }
1029 std::string_view entry(logEntry);
1030 entry.remove_prefix(entryStart);
1031 // Use split to separate the entry into its fields
1032 std::vector<std::string> logEntryFields;
1033 boost::split(logEntryFields, entry, boost::is_any_of(","),
1034 boost::token_compress_on);
1035 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001036 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001037 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001038 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001039 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001040 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001041
Jason M. Bills4851d452019-03-28 11:27:48 -07001042 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001043 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001044
Sui Chen54417b02022-03-24 14:59:52 -07001045 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001046 {
Sui Chen54417b02022-03-24 14:59:52 -07001047 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001048 return LogParseError::messageIdNotInRegistry;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001049 }
1050
Sui Chen54417b02022-03-24 14:59:52 -07001051 std::string msg = message->message;
1052
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001053 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001054 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001055 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001056 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001057 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001058 // If the first string is empty, assume there are no MessageArgs
1059 std::size_t messageArgsSize = 0;
1060 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001061 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001062 messageArgsSize = logEntryFields.size() - 1;
1063 }
1064
Ed Tanous23a21a12020-07-25 04:45:05 +00001065 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001066
1067 // Fill the MessageArgs into the Message
1068 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001069 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001070 {
1071 std::string argStr = "%" + std::to_string(++i);
1072 size_t argPos = msg.find(argStr);
1073 if (argPos != std::string::npos)
1074 {
1075 msg.replace(argPos, argStr.length(), messageArg);
1076 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001077 }
1078 }
1079
Jason M. Bills95820182019-04-22 16:25:34 -07001080 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1081 // format which matches the Redfish format except for the fractional seconds
1082 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001083 std::size_t dot = timestamp.find_first_of('.');
1084 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001085 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001086 {
Jason M. Bills95820182019-04-22 16:25:34 -07001087 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001088 }
1089
1090 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07001091 logEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1092 logEntryJson["@odata.id"] =
1093 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1094 logEntryJson["Name"] = "System Event Log Entry";
1095 logEntryJson["Id"] = logEntryID;
1096 logEntryJson["Message"] = std::move(msg);
1097 logEntryJson["MessageId"] = std::move(messageID);
1098 logEntryJson["MessageArgs"] = messageArgs;
1099 logEntryJson["EntryType"] = "Event";
1100 logEntryJson["Severity"] = message->messageSeverity;
1101 logEntryJson["Created"] = std::move(timestamp);
Jason M. Billsac992cd2022-06-24 13:31:46 -07001102 return LogParseError::success;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001103}
1104
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001105inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001106{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001107 BMCWEB_ROUTE(app,
1108 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001109 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001110 .methods(boost::beast::http::verb::get)(
1111 [&app](const crow::Request& req,
1112 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1113 query_param::QueryCapabilities capabilities = {
1114 .canDelegateTop = true,
1115 .canDelegateSkip = true,
1116 };
1117 query_param::Query delegatedQuery;
1118 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001119 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001120 {
1121 return;
1122 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001123 size_t top =
1124 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
1125 size_t skip = delegatedQuery.skip.value_or(0);
1126
Ed Tanous002d39b2022-05-31 08:59:27 -07001127 // Collections don't include the static data added by SubRoute
1128 // because it has a duplicate entry for members
1129 asyncResp->res.jsonValue["@odata.type"] =
1130 "#LogEntryCollection.LogEntryCollection";
1131 asyncResp->res.jsonValue["@odata.id"] =
1132 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1133 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1134 asyncResp->res.jsonValue["Description"] =
1135 "Collection of System Event Log Entries";
1136
1137 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1138 logEntryArray = nlohmann::json::array();
1139 // Go through the log files and create a unique ID for each
1140 // entry
1141 std::vector<std::filesystem::path> redfishLogFiles;
1142 getRedfishLogFiles(redfishLogFiles);
1143 uint64_t entryCount = 0;
1144 std::string logEntry;
1145
1146 // Oldest logs are in the last file, so start there and loop
1147 // backwards
1148 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1149 it++)
1150 {
1151 std::ifstream logStream(*it);
1152 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001153 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001154 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001155 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001156
Ed Tanous002d39b2022-05-31 08:59:27 -07001157 // Reset the unique ID on the first entry
1158 bool firstEntry = true;
1159 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001160 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001161 std::string idStr;
1162 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001163 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001164 continue;
1165 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001166 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001167
Jason M. Billsde703c52022-06-23 14:19:04 -07001168 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001169 LogParseError status =
1170 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1171 if (status == LogParseError::messageIdNotInRegistry)
1172 {
1173 continue;
1174 }
1175 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001176 {
1177 messages::internalError(asyncResp->res);
1178 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001179 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001180
Jason M. Billsde703c52022-06-23 14:19:04 -07001181 entryCount++;
1182 // Handle paging using skip (number of entries to skip from the
1183 // start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07001184 if (entryCount <= skip || entryCount > skip + top)
Jason M. Billsde703c52022-06-23 14:19:04 -07001185 {
1186 continue;
1187 }
1188
1189 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001190 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001191 }
1192 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001193 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07001194 {
1195 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1196 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001197 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07001198 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001199 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001200}
Chicago Duan336e96c2019-07-15 14:22:08 +08001201
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001202inline void requestRoutesJournalEventLogEntry(App& app)
1203{
1204 BMCWEB_ROUTE(
1205 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001206 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001207 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001208 [&app](const crow::Request& req,
1209 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1210 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001211 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001212 {
1213 return;
1214 }
1215 const std::string& targetID = param;
1216
1217 // Go through the log files and check the unique ID for each
1218 // entry to find the target entry
1219 std::vector<std::filesystem::path> redfishLogFiles;
1220 getRedfishLogFiles(redfishLogFiles);
1221 std::string logEntry;
1222
1223 // Oldest logs are in the last file, so start there and loop
1224 // backwards
1225 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1226 it++)
1227 {
1228 std::ifstream logStream(*it);
1229 if (!logStream.is_open())
1230 {
1231 continue;
1232 }
1233
1234 // Reset the unique ID on the first entry
1235 bool firstEntry = true;
1236 while (std::getline(logStream, logEntry))
1237 {
1238 std::string idStr;
1239 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001240 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001241 continue;
1242 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001243 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001244
1245 if (idStr == targetID)
1246 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001247 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001248 LogParseError status =
1249 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1250 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001251 {
1252 messages::internalError(asyncResp->res);
1253 return;
1254 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07001255 asyncResp->res.jsonValue.update(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001256 return;
1257 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001258 }
1259 }
1260 // Requested ID was not found
1261 messages::resourceMissingAtURI(asyncResp->res,
1262 crow::utility::urlFromPieces(targetID));
1263 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001264}
1265
1266inline void requestRoutesDBusEventLogEntryCollection(App& app)
1267{
1268 BMCWEB_ROUTE(app,
1269 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001270 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001271 .methods(boost::beast::http::verb::get)(
1272 [&app](const crow::Request& req,
1273 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001274 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001275 {
1276 return;
1277 }
1278 // Collections don't include the static data added by SubRoute
1279 // because it has a duplicate entry for members
1280 asyncResp->res.jsonValue["@odata.type"] =
1281 "#LogEntryCollection.LogEntryCollection";
1282 asyncResp->res.jsonValue["@odata.id"] =
1283 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1284 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1285 asyncResp->res.jsonValue["Description"] =
1286 "Collection of System Event Log Entries";
1287
1288 // DBus implementation of EventLog/Entries
1289 // Make call to Logging Service to find all log entry objects
1290 crow::connections::systemBus->async_method_call(
1291 [asyncResp](const boost::system::error_code ec,
1292 const dbus::utility::ManagedObjectType& resp) {
1293 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001294 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001295 // TODO Handle for specific error code
1296 BMCWEB_LOG_ERROR
1297 << "getLogEntriesIfaceData resp_handler got error " << ec;
1298 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001299 return;
1300 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001301 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1302 entriesArray = nlohmann::json::array();
1303 for (const auto& objectPath : resp)
1304 {
1305 const uint32_t* id = nullptr;
1306 const uint64_t* timestamp = nullptr;
1307 const uint64_t* updateTimestamp = nullptr;
1308 const std::string* severity = nullptr;
1309 const std::string* message = nullptr;
1310 const std::string* filePath = nullptr;
1311 bool resolved = false;
1312 for (const auto& interfaceMap : objectPath.second)
1313 {
1314 if (interfaceMap.first ==
1315 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001316 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001317 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001318 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001319 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001320 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001321 id = std::get_if<uint32_t>(&propertyMap.second);
1322 }
1323 else if (propertyMap.first == "Timestamp")
1324 {
1325 timestamp =
1326 std::get_if<uint64_t>(&propertyMap.second);
1327 }
1328 else if (propertyMap.first == "UpdateTimestamp")
1329 {
1330 updateTimestamp =
1331 std::get_if<uint64_t>(&propertyMap.second);
1332 }
1333 else if (propertyMap.first == "Severity")
1334 {
1335 severity = std::get_if<std::string>(
1336 &propertyMap.second);
1337 }
1338 else if (propertyMap.first == "Message")
1339 {
1340 message = std::get_if<std::string>(
1341 &propertyMap.second);
1342 }
1343 else if (propertyMap.first == "Resolved")
1344 {
1345 const bool* resolveptr =
1346 std::get_if<bool>(&propertyMap.second);
1347 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001348 {
1349 messages::internalError(asyncResp->res);
1350 return;
1351 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001352 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001353 }
1354 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001355 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001356 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001357 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001358 messages::internalError(asyncResp->res);
1359 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001360 }
1361 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001362 else if (interfaceMap.first ==
1363 "xyz.openbmc_project.Common.FilePath")
1364 {
1365 for (const auto& propertyMap : interfaceMap.second)
1366 {
1367 if (propertyMap.first == "Path")
1368 {
1369 filePath = std::get_if<std::string>(
1370 &propertyMap.second);
1371 }
1372 }
1373 }
1374 }
1375 // Object path without the
1376 // xyz.openbmc_project.Logging.Entry interface, ignore
1377 // and continue.
1378 if (id == nullptr || message == nullptr ||
1379 severity == nullptr || timestamp == nullptr ||
1380 updateTimestamp == nullptr)
1381 {
1382 continue;
1383 }
1384 entriesArray.push_back({});
1385 nlohmann::json& thisEntry = entriesArray.back();
1386 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1387 thisEntry["@odata.id"] =
1388 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1389 std::to_string(*id);
1390 thisEntry["Name"] = "System Event Log Entry";
1391 thisEntry["Id"] = std::to_string(*id);
1392 thisEntry["Message"] = *message;
1393 thisEntry["Resolved"] = resolved;
1394 thisEntry["EntryType"] = "Event";
1395 thisEntry["Severity"] =
1396 translateSeverityDbusToRedfish(*severity);
1397 thisEntry["Created"] =
1398 crow::utility::getDateTimeUintMs(*timestamp);
1399 thisEntry["Modified"] =
1400 crow::utility::getDateTimeUintMs(*updateTimestamp);
1401 if (filePath != nullptr)
1402 {
1403 thisEntry["AdditionalDataURI"] =
1404 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1405 std::to_string(*id) + "/attachment";
1406 }
1407 }
1408 std::sort(
1409 entriesArray.begin(), entriesArray.end(),
1410 [](const nlohmann::json& left, const nlohmann::json& right) {
1411 return (left["Id"] <= right["Id"]);
1412 });
1413 asyncResp->res.jsonValue["Members@odata.count"] =
1414 entriesArray.size();
1415 },
1416 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1417 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001418 });
1419}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001420
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001421inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001422{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001423 BMCWEB_ROUTE(
1424 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001425 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001426 .methods(boost::beast::http::verb::get)(
1427 [&app](const crow::Request& req,
1428 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1429 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001430 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001431 {
1432 return;
1433 }
1434 std::string entryID = param;
1435 dbus::utility::escapePathForDbus(entryID);
1436
1437 // DBus implementation of EventLog/Entries
1438 // Make call to Logging Service to find all log entry objects
1439 crow::connections::systemBus->async_method_call(
1440 [asyncResp, entryID](const boost::system::error_code ec,
1441 const dbus::utility::DBusPropertiesMap& resp) {
1442 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001443 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001444 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1445 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001446 return;
1447 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001448 if (ec)
1449 {
1450 BMCWEB_LOG_ERROR
1451 << "EventLogEntry (DBus) resp_handler got error " << ec;
1452 messages::internalError(asyncResp->res);
1453 return;
1454 }
1455 const uint32_t* id = nullptr;
1456 const uint64_t* timestamp = nullptr;
1457 const uint64_t* updateTimestamp = nullptr;
1458 const std::string* severity = nullptr;
1459 const std::string* message = nullptr;
1460 const std::string* filePath = nullptr;
1461 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001462
Ed Tanous002d39b2022-05-31 08:59:27 -07001463 for (const auto& propertyMap : resp)
1464 {
1465 if (propertyMap.first == "Id")
1466 {
1467 id = std::get_if<uint32_t>(&propertyMap.second);
1468 }
1469 else if (propertyMap.first == "Timestamp")
1470 {
1471 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1472 }
1473 else if (propertyMap.first == "UpdateTimestamp")
1474 {
1475 updateTimestamp =
1476 std::get_if<uint64_t>(&propertyMap.second);
1477 }
1478 else if (propertyMap.first == "Severity")
1479 {
1480 severity = std::get_if<std::string>(&propertyMap.second);
1481 }
1482 else if (propertyMap.first == "Message")
1483 {
1484 message = std::get_if<std::string>(&propertyMap.second);
1485 }
1486 else if (propertyMap.first == "Resolved")
1487 {
1488 const bool* resolveptr =
1489 std::get_if<bool>(&propertyMap.second);
1490 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001491 {
1492 messages::internalError(asyncResp->res);
1493 return;
1494 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001495 resolved = *resolveptr;
1496 }
1497 else if (propertyMap.first == "Path")
1498 {
1499 filePath = std::get_if<std::string>(&propertyMap.second);
1500 }
1501 }
1502 if (id == nullptr || message == nullptr || severity == nullptr ||
1503 timestamp == nullptr || updateTimestamp == nullptr)
1504 {
1505 messages::internalError(asyncResp->res);
1506 return;
1507 }
1508 asyncResp->res.jsonValue["@odata.type"] =
1509 "#LogEntry.v1_8_0.LogEntry";
1510 asyncResp->res.jsonValue["@odata.id"] =
1511 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1512 std::to_string(*id);
1513 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1514 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1515 asyncResp->res.jsonValue["Message"] = *message;
1516 asyncResp->res.jsonValue["Resolved"] = resolved;
1517 asyncResp->res.jsonValue["EntryType"] = "Event";
1518 asyncResp->res.jsonValue["Severity"] =
1519 translateSeverityDbusToRedfish(*severity);
1520 asyncResp->res.jsonValue["Created"] =
1521 crow::utility::getDateTimeUintMs(*timestamp);
1522 asyncResp->res.jsonValue["Modified"] =
1523 crow::utility::getDateTimeUintMs(*updateTimestamp);
1524 if (filePath != nullptr)
1525 {
1526 asyncResp->res.jsonValue["AdditionalDataURI"] =
1527 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1528 std::to_string(*id) + "/attachment";
1529 }
1530 },
1531 "xyz.openbmc_project.Logging",
1532 "/xyz/openbmc_project/logging/entry/" + entryID,
1533 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001534 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001535
1536 BMCWEB_ROUTE(
1537 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001538 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001539 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001540 [&app](const crow::Request& req,
1541 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1542 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001543 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001544 {
1545 return;
1546 }
1547 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001548
Ed Tanous002d39b2022-05-31 08:59:27 -07001549 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1550 resolved))
1551 {
1552 return;
1553 }
1554 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001555
Ed Tanous002d39b2022-05-31 08:59:27 -07001556 crow::connections::systemBus->async_method_call(
1557 [asyncResp, entryId](const boost::system::error_code ec) {
1558 if (ec)
1559 {
1560 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1561 messages::internalError(asyncResp->res);
1562 return;
1563 }
1564 },
1565 "xyz.openbmc_project.Logging",
1566 "/xyz/openbmc_project/logging/entry/" + entryId,
1567 "org.freedesktop.DBus.Properties", "Set",
1568 "xyz.openbmc_project.Logging.Entry", "Resolved",
1569 dbus::utility::DbusVariantType(*resolved));
1570 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001571
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001572 BMCWEB_ROUTE(
1573 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001574 .privileges(redfish::privileges::deleteLogEntry)
1575
Ed Tanous002d39b2022-05-31 08:59:27 -07001576 .methods(boost::beast::http::verb::delete_)(
1577 [&app](const crow::Request& req,
1578 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1579 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001580 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001581 {
1582 return;
1583 }
1584 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1585
1586 std::string entryID = param;
1587
1588 dbus::utility::escapePathForDbus(entryID);
1589
1590 // Process response from Logging service.
1591 auto respHandler =
1592 [asyncResp, entryID](const boost::system::error_code ec) {
1593 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1594 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001595 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001596 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001597 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001598 messages::resourceNotFound(asyncResp->res, "LogEntry",
1599 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001600 return;
1601 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001602 // TODO Handle for specific error code
1603 BMCWEB_LOG_ERROR
1604 << "EventLogEntry (DBus) doDelete respHandler got error "
1605 << ec;
1606 asyncResp->res.result(
1607 boost::beast::http::status::internal_server_error);
1608 return;
1609 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001610
Ed Tanous002d39b2022-05-31 08:59:27 -07001611 asyncResp->res.result(boost::beast::http::status::ok);
1612 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001613
Ed Tanous002d39b2022-05-31 08:59:27 -07001614 // Make call to Logging service to request Delete Log
1615 crow::connections::systemBus->async_method_call(
1616 respHandler, "xyz.openbmc_project.Logging",
1617 "/xyz/openbmc_project/logging/entry/" + entryID,
1618 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001619 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001620}
1621
1622inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001623{
George Liu0fda0f12021-11-16 10:06:17 +08001624 BMCWEB_ROUTE(
1625 app,
1626 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001627 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001628 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001629 [&app](const crow::Request& req,
1630 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1631 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001632 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001633 {
1634 return;
1635 }
1636 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1637 {
1638 asyncResp->res.result(boost::beast::http::status::bad_request);
1639 return;
1640 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001641
Ed Tanous002d39b2022-05-31 08:59:27 -07001642 std::string entryID = param;
1643 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001644
Ed Tanous002d39b2022-05-31 08:59:27 -07001645 crow::connections::systemBus->async_method_call(
1646 [asyncResp, entryID](const boost::system::error_code ec,
1647 const sdbusplus::message::unix_fd& unixfd) {
1648 if (ec.value() == EBADR)
1649 {
1650 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1651 entryID);
1652 return;
1653 }
1654 if (ec)
1655 {
1656 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1657 messages::internalError(asyncResp->res);
1658 return;
1659 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660
Ed Tanous002d39b2022-05-31 08:59:27 -07001661 int fd = -1;
1662 fd = dup(unixfd);
1663 if (fd == -1)
1664 {
1665 messages::internalError(asyncResp->res);
1666 return;
1667 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001668
Ed Tanous002d39b2022-05-31 08:59:27 -07001669 long long int size = lseek(fd, 0, SEEK_END);
1670 if (size == -1)
1671 {
1672 messages::internalError(asyncResp->res);
1673 return;
1674 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001675
Ed Tanous002d39b2022-05-31 08:59:27 -07001676 // Arbitrary max size of 64kb
1677 constexpr int maxFileSize = 65536;
1678 if (size > maxFileSize)
1679 {
1680 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1681 << maxFileSize;
1682 messages::internalError(asyncResp->res);
1683 return;
1684 }
1685 std::vector<char> data(static_cast<size_t>(size));
1686 long long int rc = lseek(fd, 0, SEEK_SET);
1687 if (rc == -1)
1688 {
1689 messages::internalError(asyncResp->res);
1690 return;
1691 }
1692 rc = read(fd, data.data(), data.size());
1693 if ((rc == -1) || (rc != size))
1694 {
1695 messages::internalError(asyncResp->res);
1696 return;
1697 }
1698 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001699
Ed Tanous002d39b2022-05-31 08:59:27 -07001700 std::string_view strData(data.data(), data.size());
1701 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001702
Ed Tanous002d39b2022-05-31 08:59:27 -07001703 asyncResp->res.addHeader("Content-Type",
1704 "application/octet-stream");
1705 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
1706 asyncResp->res.body() = std::move(output);
1707 },
1708 "xyz.openbmc_project.Logging",
1709 "/xyz/openbmc_project/logging/entry/" + entryID,
1710 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1711 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001712}
1713
Spencer Kub7028eb2021-10-26 15:27:35 +08001714constexpr const char* hostLoggerFolderPath = "/var/log/console";
1715
1716inline bool
1717 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1718 std::vector<std::filesystem::path>& hostLoggerFiles)
1719{
1720 std::error_code ec;
1721 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1722 if (ec)
1723 {
1724 BMCWEB_LOG_ERROR << ec.message();
1725 return false;
1726 }
1727 for (const std::filesystem::directory_entry& it : logPath)
1728 {
1729 std::string filename = it.path().filename();
1730 // Prefix of each log files is "log". Find the file and save the
1731 // path
Ed Tanous11ba3972022-07-11 09:50:41 -07001732 if (filename.starts_with("log"))
Spencer Kub7028eb2021-10-26 15:27:35 +08001733 {
1734 hostLoggerFiles.emplace_back(it.path());
1735 }
1736 }
1737 // As the log files rotate, they are appended with a ".#" that is higher for
1738 // the older logs. Since we start from oldest logs, sort the name in
1739 // descending order.
1740 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1741 AlphanumLess<std::string>());
1742
1743 return true;
1744}
1745
Ed Tanous02cad962022-06-30 16:50:15 -07001746inline bool getHostLoggerEntries(
1747 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1748 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001749{
1750 GzFileReader logFile;
1751
1752 // Go though all log files and expose host logs.
1753 for (const std::filesystem::path& it : hostLoggerFiles)
1754 {
1755 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1756 {
1757 BMCWEB_LOG_ERROR << "fail to expose host logs";
1758 return false;
1759 }
1760 }
1761 // Get lastMessage from constructor by getter
1762 std::string lastMessage = logFile.getLastMessage();
1763 if (!lastMessage.empty())
1764 {
1765 logCount++;
1766 if (logCount > skip && logCount <= (skip + top))
1767 {
1768 logEntries.push_back(lastMessage);
1769 }
1770 }
1771 return true;
1772}
1773
1774inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1775 const std::string& msg,
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001776 nlohmann::json::object_t& logEntryJson)
Spencer Kub7028eb2021-10-26 15:27:35 +08001777{
1778 // Fill in the log entry with the gathered data.
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001779 logEntryJson["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
1780 logEntryJson["@odata.id"] =
1781 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1782 logEntryID;
1783 logEntryJson["Name"] = "Host Logger Entry";
1784 logEntryJson["Id"] = logEntryID;
1785 logEntryJson["Message"] = msg;
1786 logEntryJson["EntryType"] = "Oem";
1787 logEntryJson["Severity"] = "OK";
1788 logEntryJson["OemRecordFormat"] = "Host Logger Entry";
Spencer Kub7028eb2021-10-26 15:27:35 +08001789}
1790
1791inline void requestRoutesSystemHostLogger(App& app)
1792{
1793 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1794 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001795 .methods(boost::beast::http::verb::get)(
1796 [&app](const crow::Request& req,
1797 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001798 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001799 {
1800 return;
1801 }
1802 asyncResp->res.jsonValue["@odata.id"] =
1803 "/redfish/v1/Systems/system/LogServices/HostLogger";
1804 asyncResp->res.jsonValue["@odata.type"] =
1805 "#LogService.v1_1_0.LogService";
1806 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1807 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1808 asyncResp->res.jsonValue["Id"] = "HostLogger";
1809 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1810 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1811 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001812}
1813
1814inline void requestRoutesSystemHostLoggerCollection(App& app)
1815{
1816 BMCWEB_ROUTE(app,
1817 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1818 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001819 .methods(boost::beast::http::verb::get)(
1820 [&app](const crow::Request& req,
1821 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1822 query_param::QueryCapabilities capabilities = {
1823 .canDelegateTop = true,
1824 .canDelegateSkip = true,
1825 };
1826 query_param::Query delegatedQuery;
1827 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001828 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001829 {
1830 return;
1831 }
1832 asyncResp->res.jsonValue["@odata.id"] =
1833 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1834 asyncResp->res.jsonValue["@odata.type"] =
1835 "#LogEntryCollection.LogEntryCollection";
1836 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1837 asyncResp->res.jsonValue["Description"] =
1838 "Collection of HostLogger Entries";
1839 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1840 logEntryArray = nlohmann::json::array();
1841 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001842
Ed Tanous002d39b2022-05-31 08:59:27 -07001843 std::vector<std::filesystem::path> hostLoggerFiles;
1844 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1845 {
1846 BMCWEB_LOG_ERROR << "fail to get host log file path";
1847 return;
1848 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001849 // If we weren't provided top and skip limits, use the defaults.
1850 size_t skip = delegatedQuery.skip.value_or(0);
1851 size_t top =
1852 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
Ed Tanous002d39b2022-05-31 08:59:27 -07001853 size_t logCount = 0;
1854 // This vector only store the entries we want to expose that
1855 // control by skip and top.
1856 std::vector<std::string> logEntries;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001857 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1858 logCount))
Ed Tanous002d39b2022-05-31 08:59:27 -07001859 {
1860 messages::internalError(asyncResp->res);
1861 return;
1862 }
1863 // If vector is empty, that means skip value larger than total
1864 // log count
1865 if (logEntries.empty())
1866 {
1867 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1868 return;
1869 }
1870 if (!logEntries.empty())
1871 {
1872 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001873 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001874 nlohmann::json::object_t hostLogEntry;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001875 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
1876 hostLogEntry);
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001877 logEntryArray.push_back(std::move(hostLogEntry));
George Liu0fda0f12021-11-16 10:06:17 +08001878 }
1879
Ed Tanous002d39b2022-05-31 08:59:27 -07001880 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001881 if (skip + top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001882 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001883 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1884 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001885 std::to_string(skip + top);
George Liu0fda0f12021-11-16 10:06:17 +08001886 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001887 }
George Liu0fda0f12021-11-16 10:06:17 +08001888 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001889}
1890
1891inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1892{
1893 BMCWEB_ROUTE(
1894 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1895 .privileges(redfish::privileges::getLogEntry)
1896 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001897 [&app](const crow::Request& req,
1898 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1899 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001900 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001901 {
1902 return;
1903 }
1904 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001905
Ed Tanous002d39b2022-05-31 08:59:27 -07001906 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001907
Ed Tanous002d39b2022-05-31 08:59:27 -07001908 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1909 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001910
Ed Tanous002d39b2022-05-31 08:59:27 -07001911 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1912 if (ec == std::errc::invalid_argument)
1913 {
1914 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1915 return;
1916 }
1917 if (ec == std::errc::result_out_of_range)
1918 {
1919 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1920 return;
1921 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001922
Ed Tanous002d39b2022-05-31 08:59:27 -07001923 std::vector<std::filesystem::path> hostLoggerFiles;
1924 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1925 {
1926 BMCWEB_LOG_ERROR << "fail to get host log file path";
1927 return;
1928 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001929
Ed Tanous002d39b2022-05-31 08:59:27 -07001930 size_t logCount = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001931 size_t top = 1;
Ed Tanous002d39b2022-05-31 08:59:27 -07001932 std::vector<std::string> logEntries;
1933 // We can get specific entry by skip and top. For example, if we
1934 // want to get nth entry, we can set skip = n-1 and top = 1 to
1935 // get that entry
1936 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1937 logCount))
1938 {
1939 messages::internalError(asyncResp->res);
1940 return;
1941 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001942
Ed Tanous002d39b2022-05-31 08:59:27 -07001943 if (!logEntries.empty())
1944 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001945 nlohmann::json::object_t hostLogEntry;
1946 fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
1947 asyncResp->res.jsonValue.update(hostLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07001948 return;
1949 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001950
Ed Tanous002d39b2022-05-31 08:59:27 -07001951 // Requested ID was not found
1952 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1953 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001954}
1955
Claire Weinanfdd26902022-03-01 14:18:25 -08001956constexpr char const* dumpManagerIface =
1957 "xyz.openbmc_project.Collection.DeleteAll";
1958inline void handleLogServicesCollectionGet(
1959 crow::App& app, const crow::Request& req,
1960 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1961{
1962 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1963 {
1964 return;
1965 }
1966 // Collections don't include the static data added by SubRoute
1967 // because it has a duplicate entry for members
1968 asyncResp->res.jsonValue["@odata.type"] =
1969 "#LogServiceCollection.LogServiceCollection";
1970 asyncResp->res.jsonValue["@odata.id"] =
1971 "/redfish/v1/Managers/bmc/LogServices";
1972 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1973 asyncResp->res.jsonValue["Description"] =
1974 "Collection of LogServices for this Manager";
1975 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
1976 logServiceArray = nlohmann::json::array();
1977
1978#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1979 logServiceArray.push_back(
1980 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
1981#endif
1982
1983 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
1984
1985#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1986 auto respHandler =
1987 [asyncResp](
1988 const boost::system::error_code ec,
1989 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
1990 if (ec)
1991 {
1992 BMCWEB_LOG_ERROR
1993 << "handleLogServicesCollectionGet respHandler got error "
1994 << ec;
1995 // Assume that getting an error simply means there are no dump
1996 // LogServices. Return without adding any error response.
1997 return;
1998 }
1999
2000 nlohmann::json& logServiceArrayLocal =
2001 asyncResp->res.jsonValue["Members"];
2002
2003 for (const std::string& path : subTreePaths)
2004 {
2005 if (path == "/xyz/openbmc_project/dump/bmc")
2006 {
2007 logServiceArrayLocal.push_back(
2008 {{"@odata.id",
2009 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
2010 }
2011 else if (path == "/xyz/openbmc_project/dump/faultlog")
2012 {
2013 logServiceArrayLocal.push_back(
2014 {{"@odata.id",
2015 "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
2016 }
2017 }
2018
2019 asyncResp->res.jsonValue["Members@odata.count"] =
2020 logServiceArrayLocal.size();
2021 };
2022
2023 crow::connections::systemBus->async_method_call(
2024 respHandler, "xyz.openbmc_project.ObjectMapper",
2025 "/xyz/openbmc_project/object_mapper",
2026 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2027 "/xyz/openbmc_project/dump", 0,
2028 std::array<const char*, 1>{dumpManagerIface});
2029#endif
2030}
2031
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002032inline void requestRoutesBMCLogServiceCollection(App& app)
2033{
2034 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002035 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002036 .methods(boost::beast::http::verb::get)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002037 std::bind_front(handleLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002038}
Ed Tanous1da66f72018-07-27 16:13:37 -07002039
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002040inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002041{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002042 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002043 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002044 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002045 [&app](const crow::Request& req,
2046 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002047 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002048 {
2049 return;
2050 }
2051 asyncResp->res.jsonValue["@odata.type"] =
2052 "#LogService.v1_1_0.LogService";
2053 asyncResp->res.jsonValue["@odata.id"] =
2054 "/redfish/v1/Managers/bmc/LogServices/Journal";
2055 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2056 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2057 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2058 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302059
Ed Tanous002d39b2022-05-31 08:59:27 -07002060 std::pair<std::string, std::string> redfishDateTimeOffset =
2061 crow::utility::getDateTimeOffsetNow();
2062 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2063 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2064 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302065
Ed Tanous002d39b2022-05-31 08:59:27 -07002066 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2067 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2068 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002069}
Jason M. Billse1f26342018-07-18 12:12:00 -07002070
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002071static int
2072 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2073 sd_journal* journal,
2074 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002075{
2076 // Get the Log Entry contents
2077 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002078
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002079 std::string message;
2080 std::string_view syslogID;
2081 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2082 if (ret < 0)
2083 {
2084 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2085 << strerror(-ret);
2086 }
2087 if (!syslogID.empty())
2088 {
2089 message += std::string(syslogID) + ": ";
2090 }
2091
Ed Tanous39e77502019-03-04 17:35:53 -08002092 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002093 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002094 if (ret < 0)
2095 {
2096 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2097 return 1;
2098 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002099 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002100
2101 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002102 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002103 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002104 if (ret < 0)
2105 {
2106 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002107 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002108
2109 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002110 std::string entryTimeStr;
2111 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002112 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002113 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002114 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002115
2116 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07002117 bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
2118 bmcJournalLogEntryJson["@odata.id"] =
2119 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2120 bmcJournalLogEntryID;
2121 bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2122 bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2123 bmcJournalLogEntryJson["Message"] = std::move(message);
2124 bmcJournalLogEntryJson["EntryType"] = "Oem";
2125 bmcJournalLogEntryJson["Severity"] = severity <= 2 ? "Critical"
2126 : severity <= 4 ? "Warning"
2127 : "OK";
2128 bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2129 bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
Jason M. Billse1f26342018-07-18 12:12:00 -07002130 return 0;
2131}
2132
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002133inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002134{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002135 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002136 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002137 .methods(boost::beast::http::verb::get)(
2138 [&app](const crow::Request& req,
2139 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2140 query_param::QueryCapabilities capabilities = {
2141 .canDelegateTop = true,
2142 .canDelegateSkip = true,
2143 };
2144 query_param::Query delegatedQuery;
2145 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002146 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002147 {
2148 return;
2149 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07002150
2151 size_t skip = delegatedQuery.skip.value_or(0);
2152 size_t top =
2153 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
2154
Ed Tanous002d39b2022-05-31 08:59:27 -07002155 // Collections don't include the static data added by SubRoute
2156 // because it has a duplicate entry for members
2157 asyncResp->res.jsonValue["@odata.type"] =
2158 "#LogEntryCollection.LogEntryCollection";
2159 asyncResp->res.jsonValue["@odata.id"] =
2160 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2161 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2162 asyncResp->res.jsonValue["Description"] =
2163 "Collection of BMC Journal Entries";
2164 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2165 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002166
Ed Tanous002d39b2022-05-31 08:59:27 -07002167 // Go through the journal and use the timestamp to create a
2168 // unique ID for each entry
2169 sd_journal* journalTmp = nullptr;
2170 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2171 if (ret < 0)
2172 {
2173 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2174 messages::internalError(asyncResp->res);
2175 return;
2176 }
2177 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2178 journalTmp, sd_journal_close);
2179 journalTmp = nullptr;
2180 uint64_t entryCount = 0;
2181 // Reset the unique ID on the first entry
2182 bool firstEntry = true;
2183 SD_JOURNAL_FOREACH(journal.get())
2184 {
2185 entryCount++;
2186 // Handle paging using skip (number of entries to skip from
2187 // the start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07002188 if (entryCount <= skip || entryCount > skip + top)
George Liu0fda0f12021-11-16 10:06:17 +08002189 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002190 continue;
2191 }
2192
2193 std::string idStr;
2194 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2195 {
2196 continue;
2197 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002198 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002199
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002200 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002201 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2202 bmcJournalLogEntry) != 0)
2203 {
George Liu0fda0f12021-11-16 10:06:17 +08002204 messages::internalError(asyncResp->res);
2205 return;
2206 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002207 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002208 }
2209 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07002210 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07002211 {
2212 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2213 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07002214 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07002215 }
George Liu0fda0f12021-11-16 10:06:17 +08002216 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002217}
Jason M. Billse1f26342018-07-18 12:12:00 -07002218
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002219inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002220{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002221 BMCWEB_ROUTE(app,
2222 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002223 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002224 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002225 [&app](const crow::Request& req,
2226 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2227 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002228 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002229 {
2230 return;
2231 }
2232 // Convert the unique ID back to a timestamp to find the entry
2233 uint64_t ts = 0;
2234 uint64_t index = 0;
2235 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2236 {
2237 return;
2238 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002239
Ed Tanous002d39b2022-05-31 08:59:27 -07002240 sd_journal* journalTmp = nullptr;
2241 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2242 if (ret < 0)
2243 {
2244 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2245 messages::internalError(asyncResp->res);
2246 return;
2247 }
2248 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2249 journalTmp, sd_journal_close);
2250 journalTmp = nullptr;
2251 // Go to the timestamp in the log and move to the entry at the
2252 // index tracking the unique ID
2253 std::string idStr;
2254 bool firstEntry = true;
2255 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2256 if (ret < 0)
2257 {
2258 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2259 << strerror(-ret);
2260 messages::internalError(asyncResp->res);
2261 return;
2262 }
2263 for (uint64_t i = 0; i <= index; i++)
2264 {
2265 sd_journal_next(journal.get());
2266 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2267 {
2268 messages::internalError(asyncResp->res);
2269 return;
2270 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002271 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002272 }
2273 // Confirm that the entry ID matches what was requested
2274 if (idStr != entryID)
2275 {
2276 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2277 return;
2278 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002279
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002280 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002281 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002282 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002283 {
2284 messages::internalError(asyncResp->res);
2285 return;
2286 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07002287 asyncResp->res.jsonValue.update(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002288 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002289}
2290
Claire Weinanfdd26902022-03-01 14:18:25 -08002291inline void
2292 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2293 const std::string& dumpType)
2294{
2295 std::string dumpPath;
2296 std::string overWritePolicy;
2297 bool collectDiagnosticDataSupported = false;
2298
2299 if (dumpType == "BMC")
2300 {
2301 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2302 overWritePolicy = "WrapsWhenFull";
2303 collectDiagnosticDataSupported = true;
2304 }
2305 else if (dumpType == "FaultLog")
2306 {
2307 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2308 overWritePolicy = "Unknown";
2309 collectDiagnosticDataSupported = false;
2310 }
2311 else if (dumpType == "System")
2312 {
2313 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2314 overWritePolicy = "WrapsWhenFull";
2315 collectDiagnosticDataSupported = true;
2316 }
2317 else
2318 {
2319 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2320 << dumpType;
2321 messages::internalError(asyncResp->res);
2322 return;
2323 }
2324
2325 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2326 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2327 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2328 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2329 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2330 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2331
2332 std::pair<std::string, std::string> redfishDateTimeOffset =
2333 crow::utility::getDateTimeOffsetNow();
2334 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2335 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2336 redfishDateTimeOffset.second;
2337
2338 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2339 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2340 dumpPath + "/Actions/LogService.ClearLog";
2341
2342 if (collectDiagnosticDataSupported)
2343 {
2344 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2345 ["target"] =
2346 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2347 }
2348}
2349
2350inline void handleLogServicesDumpServiceGet(
2351 crow::App& app, const std::string& dumpType, const crow::Request& req,
2352 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2353{
2354 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2355 {
2356 return;
2357 }
2358 getDumpServiceInfo(asyncResp, dumpType);
2359}
2360
2361inline void handleLogServicesDumpEntriesCollectionGet(
2362 crow::App& app, const std::string& dumpType, const crow::Request& req,
2363 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2364{
2365 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2366 {
2367 return;
2368 }
2369 getDumpEntryCollection(asyncResp, dumpType);
2370}
2371
2372inline void handleLogServicesDumpEntryGet(
2373 crow::App& app, const std::string& dumpType, const crow::Request& req,
2374 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2375 const std::string& dumpId)
2376{
2377 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2378 {
2379 return;
2380 }
2381 getDumpEntryById(asyncResp, dumpId, dumpType);
2382}
2383
2384inline void handleLogServicesDumpEntryDelete(
2385 crow::App& app, const std::string& dumpType, const crow::Request& req,
2386 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2387 const std::string& dumpId)
2388{
2389 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2390 {
2391 return;
2392 }
2393 deleteDumpEntry(asyncResp, dumpId, dumpType);
2394}
2395
2396inline void handleLogServicesDumpCollectDiagnosticDataPost(
2397 crow::App& app, const std::string& dumpType, const crow::Request& req,
2398 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2399{
2400 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2401 {
2402 return;
2403 }
2404 createDump(asyncResp, req, dumpType);
2405}
2406
2407inline void handleLogServicesDumpClearLogPost(
2408 crow::App& app, const std::string& dumpType, const crow::Request& req,
2409 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2410{
2411 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2412 {
2413 return;
2414 }
2415 clearDump(asyncResp, dumpType);
2416}
2417
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002418inline void requestRoutesBMCDumpService(App& app)
2419{
2420 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002421 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002422 .methods(boost::beast::http::verb::get)(std::bind_front(
2423 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002424}
2425
2426inline void requestRoutesBMCDumpEntryCollection(App& app)
2427{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002428 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002429 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002430 .methods(boost::beast::http::verb::get)(std::bind_front(
2431 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002432}
2433
2434inline void requestRoutesBMCDumpEntry(App& app)
2435{
2436 BMCWEB_ROUTE(app,
2437 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002438 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002439 .methods(boost::beast::http::verb::get)(std::bind_front(
2440 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2441
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002442 BMCWEB_ROUTE(app,
2443 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002444 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002445 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2446 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002447}
2448
2449inline void requestRoutesBMCDumpCreate(App& app)
2450{
George Liu0fda0f12021-11-16 10:06:17 +08002451 BMCWEB_ROUTE(
2452 app,
2453 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002454 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002455 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002456 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2457 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002458}
2459
2460inline void requestRoutesBMCDumpClear(App& app)
2461{
George Liu0fda0f12021-11-16 10:06:17 +08002462 BMCWEB_ROUTE(
2463 app,
2464 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002465 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002466 .methods(boost::beast::http::verb::post)(std::bind_front(
2467 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2468}
2469
2470inline void requestRoutesFaultLogDumpService(App& app)
2471{
2472 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2473 .privileges(redfish::privileges::getLogService)
2474 .methods(boost::beast::http::verb::get)(std::bind_front(
2475 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2476}
2477
2478inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2479{
2480 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2481 .privileges(redfish::privileges::getLogEntryCollection)
2482 .methods(boost::beast::http::verb::get)(
2483 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2484 std::ref(app), "FaultLog"));
2485}
2486
2487inline void requestRoutesFaultLogDumpEntry(App& app)
2488{
2489 BMCWEB_ROUTE(app,
2490 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2491 .privileges(redfish::privileges::getLogEntry)
2492 .methods(boost::beast::http::verb::get)(std::bind_front(
2493 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2494
2495 BMCWEB_ROUTE(app,
2496 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2497 .privileges(redfish::privileges::deleteLogEntry)
2498 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2499 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2500}
2501
2502inline void requestRoutesFaultLogDumpClear(App& app)
2503{
2504 BMCWEB_ROUTE(
2505 app,
2506 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2507 .privileges(redfish::privileges::postLogService)
2508 .methods(boost::beast::http::verb::post)(std::bind_front(
2509 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002510}
2511
2512inline void requestRoutesSystemDumpService(App& app)
2513{
2514 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002515 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002516 .methods(boost::beast::http::verb::get)(
2517 [&app](const crow::Request& req,
2518 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002519 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002520 {
2521 return;
2522 }
2523 asyncResp->res.jsonValue["@odata.id"] =
2524 "/redfish/v1/Systems/system/LogServices/Dump";
2525 asyncResp->res.jsonValue["@odata.type"] =
2526 "#LogService.v1_2_0.LogService";
2527 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2528 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2529 asyncResp->res.jsonValue["Id"] = "Dump";
2530 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302531
Ed Tanous002d39b2022-05-31 08:59:27 -07002532 std::pair<std::string, std::string> redfishDateTimeOffset =
2533 crow::utility::getDateTimeOffsetNow();
2534 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2535 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2536 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302537
Ed Tanous002d39b2022-05-31 08:59:27 -07002538 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2539 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2540 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2541 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002542
Ed Tanous002d39b2022-05-31 08:59:27 -07002543 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2544 ["target"] =
2545 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002546 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002547}
2548
2549inline void requestRoutesSystemDumpEntryCollection(App& app)
2550{
2551
2552 /**
2553 * Functions triggers appropriate requests on DBus
2554 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002555 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002556 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002557 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002558 [&app](const crow::Request& req,
2559 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002560 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002561 {
2562 return;
2563 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002564 getDumpEntryCollection(asyncResp, "System");
2565 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002566}
2567
2568inline void requestRoutesSystemDumpEntry(App& app)
2569{
2570 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002571 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002572 .privileges(redfish::privileges::getLogEntry)
2573
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002574 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002575 [&app](const crow::Request& req,
2576 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2577 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002578 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002579 {
2580 return;
2581 }
2582 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002583 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002584
2585 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002586 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002587 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002588 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002589 [&app](const crow::Request& req,
2590 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2591 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002592 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002593 {
2594 return;
2595 }
2596 deleteDumpEntry(asyncResp, param, "system");
2597 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002598}
2599
2600inline void requestRoutesSystemDumpCreate(App& app)
2601{
George Liu0fda0f12021-11-16 10:06:17 +08002602 BMCWEB_ROUTE(
2603 app,
2604 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002605 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002606 .methods(boost::beast::http::verb::post)(
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 }
2613 createDump(asyncResp, req, "System");
2614 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002615}
2616
2617inline void requestRoutesSystemDumpClear(App& app)
2618{
George Liu0fda0f12021-11-16 10:06:17 +08002619 BMCWEB_ROUTE(
2620 app,
2621 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002622 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002623 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002624 [&app](const crow::Request& req,
2625 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002626
Ed Tanous45ca1b82022-03-25 13:07:27 -07002627 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002628 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002629 {
2630 return;
2631 }
2632 clearDump(asyncResp, "System");
2633 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002634}
2635
2636inline void requestRoutesCrashdumpService(App& app)
2637{
2638 // Note: Deviated from redfish privilege registry for GET & HEAD
2639 // method for security reasons.
2640 /**
2641 * Functions triggers appropriate requests on DBus
2642 */
2643 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002644 // This is incorrect, should be:
2645 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002646 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002647 .methods(boost::beast::http::verb::get)(
2648 [&app](const crow::Request& req,
2649 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002650 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002651 {
2652 return;
2653 }
2654 // Copy over the static data to include the entries added by
2655 // SubRoute
2656 asyncResp->res.jsonValue["@odata.id"] =
2657 "/redfish/v1/Systems/system/LogServices/Crashdump";
2658 asyncResp->res.jsonValue["@odata.type"] =
2659 "#LogService.v1_2_0.LogService";
2660 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2661 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2662 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2663 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2664 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302665
Ed Tanous002d39b2022-05-31 08:59:27 -07002666 std::pair<std::string, std::string> redfishDateTimeOffset =
2667 crow::utility::getDateTimeOffsetNow();
2668 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2669 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2670 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302671
Ed Tanous002d39b2022-05-31 08:59:27 -07002672 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2673 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2674 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2675 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2676 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2677 ["target"] =
2678 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002679 });
2680}
2681
2682void inline requestRoutesCrashdumpClear(App& app)
2683{
George Liu0fda0f12021-11-16 10:06:17 +08002684 BMCWEB_ROUTE(
2685 app,
2686 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002687 // This is incorrect, should be:
2688 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002689 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002690 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002691 [&app](const crow::Request& req,
2692 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002693 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002694 {
2695 return;
2696 }
2697 crow::connections::systemBus->async_method_call(
2698 [asyncResp](const boost::system::error_code ec,
2699 const std::string&) {
2700 if (ec)
2701 {
2702 messages::internalError(asyncResp->res);
2703 return;
2704 }
2705 messages::success(asyncResp->res);
2706 },
2707 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2708 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002709}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002710
zhanghch058d1b46d2021-04-01 11:18:24 +08002711static void
2712 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2713 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002714{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002715 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002716 [asyncResp, logID,
2717 &logEntryJson](const boost::system::error_code ec,
2718 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002719 if (ec)
2720 {
2721 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2722 if (ec.value() ==
2723 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002724 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002725 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002726 }
2727 else
2728 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002729 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002730 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002731 return;
2732 }
2733
2734 std::string timestamp{};
2735 std::string filename{};
2736 std::string logfile{};
2737 parseCrashdumpParameters(params, filename, timestamp, logfile);
2738
2739 if (filename.empty() || timestamp.empty())
2740 {
2741 messages::resourceMissingAtURI(asyncResp->res,
2742 crow::utility::urlFromPieces(logID));
2743 return;
2744 }
2745
2746 std::string crashdumpURI =
2747 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2748 logID + "/" + filename;
Jason M. Bills84afc482022-06-24 12:38:23 -07002749 nlohmann::json::object_t logEntry;
2750 logEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
2751 logEntry["@odata.id"] =
2752 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
2753 logEntry["Name"] = "CPU Crashdump";
2754 logEntry["Id"] = logID;
2755 logEntry["EntryType"] = "Oem";
2756 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2757 logEntry["DiagnosticDataType"] = "OEM";
2758 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2759 logEntry["Created"] = std::move(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07002760
2761 // If logEntryJson references an array of LogEntry resources
2762 // ('Members' list), then push this as a new entry, otherwise set it
2763 // directly
2764 if (logEntryJson.is_array())
2765 {
2766 logEntryJson.push_back(logEntry);
2767 asyncResp->res.jsonValue["Members@odata.count"] =
2768 logEntryJson.size();
2769 }
2770 else
2771 {
Jason M. Billsd405bb52022-06-24 10:52:05 -07002772 logEntryJson.update(logEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002773 }
2774 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002775 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002776 std::move(getStoredLogCallback), crashdumpObject,
2777 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002778 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002779}
2780
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002781inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002782{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002783 // Note: Deviated from redfish privilege registry for GET & HEAD
2784 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002785 /**
2786 * Functions triggers appropriate requests on DBus
2787 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002788 BMCWEB_ROUTE(app,
2789 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002790 // This is incorrect, should be.
2791 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002792 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002793 .methods(boost::beast::http::verb::get)(
2794 [&app](const crow::Request& req,
2795 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002796 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002797 {
2798 return;
2799 }
2800 crow::connections::systemBus->async_method_call(
2801 [asyncResp](const boost::system::error_code ec,
2802 const std::vector<std::string>& resp) {
2803 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002804 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002805 if (ec.value() !=
2806 boost::system::errc::no_such_file_or_directory)
2807 {
2808 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2809 << ec.message();
2810 messages::internalError(asyncResp->res);
2811 return;
2812 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002813 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002814 asyncResp->res.jsonValue["@odata.type"] =
2815 "#LogEntryCollection.LogEntryCollection";
2816 asyncResp->res.jsonValue["@odata.id"] =
2817 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2818 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2819 asyncResp->res.jsonValue["Description"] =
2820 "Collection of Crashdump Entries";
2821 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2822 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002823
Ed Tanous002d39b2022-05-31 08:59:27 -07002824 for (const std::string& path : resp)
2825 {
2826 const sdbusplus::message::object_path objPath(path);
2827 // Get the log ID
2828 std::string logID = objPath.filename();
2829 if (logID.empty())
2830 {
2831 continue;
2832 }
2833 // Add the log entry to the array
2834 logCrashdumpEntry(asyncResp, logID,
2835 asyncResp->res.jsonValue["Members"]);
2836 }
2837 },
2838 "xyz.openbmc_project.ObjectMapper",
2839 "/xyz/openbmc_project/object_mapper",
2840 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2841 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002842 });
2843}
Ed Tanous1da66f72018-07-27 16:13:37 -07002844
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002845inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002846{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002847 // Note: Deviated from redfish privilege registry for GET & HEAD
2848 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002849
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002850 BMCWEB_ROUTE(
2851 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002852 // this is incorrect, should be
2853 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002854 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002855 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002856 [&app](const crow::Request& req,
2857 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2858 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002859 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002860 {
2861 return;
2862 }
2863 const std::string& logID = param;
2864 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2865 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002866}
Ed Tanous1da66f72018-07-27 16:13:37 -07002867
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002868inline void requestRoutesCrashdumpFile(App& app)
2869{
2870 // Note: Deviated from redfish privilege registry for GET & HEAD
2871 // method for security reasons.
2872 BMCWEB_ROUTE(
2873 app,
2874 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002875 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002876 .methods(boost::beast::http::verb::get)(
Nan Zhoua4ce1142022-08-02 18:45:25 +00002877 [](const crow::Request& req,
2878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2879 const std::string& logID, const std::string& fileName) {
Shounak Mitra2a9beee2022-07-20 18:41:30 +00002880 // Do not call getRedfishRoute here since the crashdump file is not a
2881 // Redfish resource.
Ed Tanous002d39b2022-05-31 08:59:27 -07002882 auto getStoredLogCallback =
2883 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2884 const boost::system::error_code ec,
2885 const std::vector<
2886 std::pair<std::string, dbus::utility::DbusVariantType>>&
2887 resp) {
2888 if (ec)
2889 {
2890 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2891 messages::internalError(asyncResp->res);
2892 return;
2893 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002894
Ed Tanous002d39b2022-05-31 08:59:27 -07002895 std::string dbusFilename{};
2896 std::string dbusTimestamp{};
2897 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002898
Ed Tanous002d39b2022-05-31 08:59:27 -07002899 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2900 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002901
Ed Tanous002d39b2022-05-31 08:59:27 -07002902 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2903 dbusFilepath.empty())
2904 {
2905 messages::resourceMissingAtURI(asyncResp->res, url);
2906 return;
2907 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002908
Ed Tanous002d39b2022-05-31 08:59:27 -07002909 // Verify the file name parameter is correct
2910 if (fileName != dbusFilename)
2911 {
2912 messages::resourceMissingAtURI(asyncResp->res, url);
2913 return;
2914 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002915
Ed Tanous002d39b2022-05-31 08:59:27 -07002916 if (!std::filesystem::exists(dbusFilepath))
2917 {
2918 messages::resourceMissingAtURI(asyncResp->res, url);
2919 return;
2920 }
2921 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2922 asyncResp->res.body() =
2923 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002924
Ed Tanous002d39b2022-05-31 08:59:27 -07002925 // Configure this to be a file download when accessed
2926 // from a browser
2927 asyncResp->res.addHeader("Content-Disposition", "attachment");
2928 };
2929 crow::connections::systemBus->async_method_call(
2930 std::move(getStoredLogCallback), crashdumpObject,
2931 crashdumpPath + std::string("/") + logID,
2932 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2933 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002934}
2935
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002936enum class OEMDiagnosticType
2937{
2938 onDemand,
2939 telemetry,
2940 invalid,
2941};
2942
Ed Tanousf7725d72022-03-07 12:46:00 -08002943inline OEMDiagnosticType
2944 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002945{
2946 if (oemDiagStr == "OnDemand")
2947 {
2948 return OEMDiagnosticType::onDemand;
2949 }
2950 if (oemDiagStr == "Telemetry")
2951 {
2952 return OEMDiagnosticType::telemetry;
2953 }
2954
2955 return OEMDiagnosticType::invalid;
2956}
2957
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002958inline void requestRoutesCrashdumpCollect(App& app)
2959{
2960 // Note: Deviated from redfish privilege registry for GET & HEAD
2961 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002962 BMCWEB_ROUTE(
2963 app,
2964 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002965 // The below is incorrect; Should be ConfigureManager
2966 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002967 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002968 .methods(boost::beast::http::verb::post)(
2969 [&app](const crow::Request& req,
2970 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002971 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002972 {
2973 return;
2974 }
2975 std::string diagnosticDataType;
2976 std::string oemDiagnosticDataType;
2977 if (!redfish::json_util::readJsonAction(
2978 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
2979 "OEMDiagnosticDataType", oemDiagnosticDataType))
2980 {
2981 return;
2982 }
2983
2984 if (diagnosticDataType != "OEM")
2985 {
2986 BMCWEB_LOG_ERROR
2987 << "Only OEM DiagnosticDataType supported for Crashdump";
2988 messages::actionParameterValueFormatError(
2989 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2990 "CollectDiagnosticData");
2991 return;
2992 }
2993
2994 OEMDiagnosticType oemDiagType =
2995 getOEMDiagnosticType(oemDiagnosticDataType);
2996
2997 std::string iface;
2998 std::string method;
2999 std::string taskMatchStr;
3000 if (oemDiagType == OEMDiagnosticType::onDemand)
3001 {
3002 iface = crashdumpOnDemandInterface;
3003 method = "GenerateOnDemandLog";
3004 taskMatchStr = "type='signal',"
3005 "interface='org.freedesktop.DBus.Properties',"
3006 "member='PropertiesChanged',"
3007 "arg0namespace='com.intel.crashdump'";
3008 }
3009 else if (oemDiagType == OEMDiagnosticType::telemetry)
3010 {
3011 iface = crashdumpTelemetryInterface;
3012 method = "GenerateTelemetryLog";
3013 taskMatchStr = "type='signal',"
3014 "interface='org.freedesktop.DBus.Properties',"
3015 "member='PropertiesChanged',"
3016 "arg0namespace='com.intel.crashdump'";
3017 }
3018 else
3019 {
3020 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3021 << oemDiagnosticDataType;
3022 messages::actionParameterValueFormatError(
3023 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3024 "CollectDiagnosticData");
3025 return;
3026 }
3027
3028 auto collectCrashdumpCallback =
3029 [asyncResp, payload(task::Payload(req)),
3030 taskMatchStr](const boost::system::error_code ec,
3031 const std::string&) mutable {
3032 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003033 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003034 if (ec.value() == boost::system::errc::operation_not_supported)
3035 {
3036 messages::resourceInStandby(asyncResp->res);
3037 }
3038 else if (ec.value() ==
3039 boost::system::errc::device_or_resource_busy)
3040 {
3041 messages::serviceTemporarilyUnavailable(asyncResp->res,
3042 "60");
3043 }
3044 else
3045 {
3046 messages::internalError(asyncResp->res);
3047 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003048 return;
3049 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003050 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -05003051 [](boost::system::error_code err, sdbusplus::message_t&,
Ed Tanous002d39b2022-05-31 08:59:27 -07003052 const std::shared_ptr<task::TaskData>& taskData) {
3053 if (!err)
3054 {
3055 taskData->messages.emplace_back(messages::taskCompletedOK(
3056 std::to_string(taskData->index)));
3057 taskData->state = "Completed";
3058 }
3059 return task::completed;
3060 },
3061 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003062
Ed Tanous002d39b2022-05-31 08:59:27 -07003063 task->startTimer(std::chrono::minutes(5));
3064 task->populateResp(asyncResp->res);
3065 task->payload.emplace(std::move(payload));
3066 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003067
Ed Tanous002d39b2022-05-31 08:59:27 -07003068 crow::connections::systemBus->async_method_call(
3069 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3070 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003071 });
3072}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003073
Andrew Geisslercb92c032018-08-17 07:56:14 -07003074/**
3075 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3076 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003077inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003078{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003079 /**
3080 * Function handles POST method request.
3081 * The Clear Log actions does not require any parameter.The action deletes
3082 * all entries found in the Entries collection for this Log Service.
3083 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003084
George Liu0fda0f12021-11-16 10:06:17 +08003085 BMCWEB_ROUTE(
3086 app,
3087 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003088 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003089 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003090 [&app](const crow::Request& req,
3091 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003092 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003093 {
3094 return;
3095 }
3096 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003097
Ed Tanous002d39b2022-05-31 08:59:27 -07003098 // Process response from Logging service.
3099 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3100 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3101 if (ec)
3102 {
3103 // TODO Handle for specific error code
3104 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3105 asyncResp->res.result(
3106 boost::beast::http::status::internal_server_error);
3107 return;
3108 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003109
Ed Tanous002d39b2022-05-31 08:59:27 -07003110 asyncResp->res.result(boost::beast::http::status::no_content);
3111 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003112
Ed Tanous002d39b2022-05-31 08:59:27 -07003113 // Make call to Logging service to request Clear Log
3114 crow::connections::systemBus->async_method_call(
3115 respHandler, "xyz.openbmc_project.Logging",
3116 "/xyz/openbmc_project/logging",
3117 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3118 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003119}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003120
3121/****************************************************
3122 * Redfish PostCode interfaces
3123 * using DBUS interface: getPostCodesTS
3124 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003125inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003126{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003127 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003128 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003129 .methods(boost::beast::http::verb::get)(
3130 [&app](const crow::Request& req,
3131 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003132 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003133 {
3134 return;
3135 }
Ed Tanous14766872022-03-15 10:44:42 -07003136
Ed Tanous002d39b2022-05-31 08:59:27 -07003137 asyncResp->res.jsonValue["@odata.id"] =
3138 "/redfish/v1/Systems/system/LogServices/PostCodes";
3139 asyncResp->res.jsonValue["@odata.type"] =
3140 "#LogService.v1_1_0.LogService";
3141 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3142 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3143 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3144 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3145 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3146 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303147
Ed Tanous002d39b2022-05-31 08:59:27 -07003148 std::pair<std::string, std::string> redfishDateTimeOffset =
3149 crow::utility::getDateTimeOffsetNow();
3150 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3151 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3152 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303153
Ed Tanous002d39b2022-05-31 08:59:27 -07003154 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3155 {"target",
3156 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003157 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003158}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003159
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003160inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003161{
George Liu0fda0f12021-11-16 10:06:17 +08003162 BMCWEB_ROUTE(
3163 app,
3164 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003165 // The following privilege is incorrect; It should be ConfigureManager
3166 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003167 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003168 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003169 [&app](const crow::Request& req,
3170 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003171 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003172 {
3173 return;
3174 }
3175 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003176
Ed Tanous002d39b2022-05-31 08:59:27 -07003177 // Make call to post-code service to request clear all
3178 crow::connections::systemBus->async_method_call(
3179 [asyncResp](const boost::system::error_code ec) {
3180 if (ec)
3181 {
3182 // TODO Handle for specific error code
3183 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3184 << ec;
3185 asyncResp->res.result(
3186 boost::beast::http::status::internal_server_error);
3187 messages::internalError(asyncResp->res);
3188 return;
3189 }
3190 },
3191 "xyz.openbmc_project.State.Boot.PostCode0",
3192 "/xyz/openbmc_project/State/Boot/PostCode0",
3193 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3194 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003195}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003196
3197static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003198 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303199 const boost::container::flat_map<
3200 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003201 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3202 const uint64_t skip = 0, const uint64_t top = 0)
3203{
3204 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003205 const registries::Message* message =
3206 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003207
3208 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003209 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003210
3211 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303212 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3213 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003214 {
3215 currentCodeIndex++;
3216 std::string postcodeEntryID =
3217 "B" + std::to_string(bootIndex) + "-" +
3218 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3219
3220 uint64_t usecSinceEpoch = code.first;
3221 uint64_t usTimeOffset = 0;
3222
3223 if (1 == currentCodeIndex)
3224 { // already incremented
3225 firstCodeTimeUs = code.first;
3226 }
3227 else
3228 {
3229 usTimeOffset = code.first - firstCodeTimeUs;
3230 }
3231
3232 // skip if no specific codeIndex is specified and currentCodeIndex does
3233 // not fall between top and skip
3234 if ((codeIndex == 0) &&
3235 (currentCodeIndex <= skip || currentCodeIndex > top))
3236 {
3237 continue;
3238 }
3239
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003240 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003241 // currentIndex
3242 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3243 {
3244 // This is done for simplicity. 1st entry is needed to calculate
3245 // time offset. To improve efficiency, one can get to the entry
3246 // directly (possibly with flatmap's nth method)
3247 continue;
3248 }
3249
3250 // currentCodeIndex is within top and skip or equal to specified code
3251 // index
3252
3253 // Get the Created time from the timestamp
3254 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003255 entryTimeStr =
3256 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003257
3258 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3259 std::ostringstream hexCode;
3260 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303261 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003262 std::ostringstream timeOffsetStr;
3263 // Set Fixed -Point Notation
3264 timeOffsetStr << std::fixed;
3265 // Set precision to 4 digits
3266 timeOffsetStr << std::setprecision(4);
3267 // Add double to stream
3268 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3269 std::vector<std::string> messageArgs = {
3270 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3271
3272 // Get MessageArgs template from message registry
3273 std::string msg;
3274 if (message != nullptr)
3275 {
3276 msg = message->message;
3277
3278 // fill in this post code value
3279 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003280 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003281 {
3282 std::string argStr = "%" + std::to_string(++i);
3283 size_t argPos = msg.find(argStr);
3284 if (argPos != std::string::npos)
3285 {
3286 msg.replace(argPos, argStr.length(), messageArg);
3287 }
3288 }
3289 }
3290
Tim Leed4342a92020-04-27 11:47:58 +08003291 // Get Severity template from message registry
3292 std::string severity;
3293 if (message != nullptr)
3294 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003295 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003296 }
3297
ZhikuiRena3316fc2020-01-29 14:58:08 -08003298 // add to AsyncResp
3299 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003300 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills84afc482022-06-24 12:38:23 -07003301 bmcLogEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
3302 bmcLogEntry["@odata.id"] =
3303 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3304 postcodeEntryID;
3305 bmcLogEntry["Name"] = "POST Code Log Entry";
3306 bmcLogEntry["Id"] = postcodeEntryID;
3307 bmcLogEntry["Message"] = std::move(msg);
3308 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3309 bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3310 bmcLogEntry["EntryType"] = "Event";
3311 bmcLogEntry["Severity"] = std::move(severity);
3312 bmcLogEntry["Created"] = entryTimeStr;
George Liu647b3cd2021-07-05 12:43:56 +08003313 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3314 {
3315 bmcLogEntry["AdditionalDataURI"] =
3316 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3317 postcodeEntryID + "/attachment";
3318 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003319 }
3320}
3321
zhanghch058d1b46d2021-04-01 11:18:24 +08003322static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003323 const uint16_t bootIndex,
3324 const uint64_t codeIndex)
3325{
3326 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303327 [aResp, bootIndex,
3328 codeIndex](const boost::system::error_code ec,
3329 const boost::container::flat_map<
3330 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3331 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003332 if (ec)
3333 {
3334 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3335 messages::internalError(aResp->res);
3336 return;
3337 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003338
Ed Tanous002d39b2022-05-31 08:59:27 -07003339 // skip the empty postcode boots
3340 if (postcode.empty())
3341 {
3342 return;
3343 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003344
Ed Tanous002d39b2022-05-31 08:59:27 -07003345 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003346
Ed Tanous002d39b2022-05-31 08:59:27 -07003347 aResp->res.jsonValue["Members@odata.count"] =
3348 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003349 },
Jonathan Doman15124762021-01-07 17:54:17 -08003350 "xyz.openbmc_project.State.Boot.PostCode0",
3351 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003352 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3353 bootIndex);
3354}
3355
zhanghch058d1b46d2021-04-01 11:18:24 +08003356static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003357 const uint16_t bootIndex,
3358 const uint16_t bootCount,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003359 const uint64_t entryCount, size_t skip,
3360 size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003361{
3362 crow::connections::systemBus->async_method_call(
3363 [aResp, bootIndex, bootCount, entryCount, skip,
3364 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303365 const boost::container::flat_map<
3366 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3367 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003368 if (ec)
3369 {
3370 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3371 messages::internalError(aResp->res);
3372 return;
3373 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003374
Ed Tanous002d39b2022-05-31 08:59:27 -07003375 uint64_t endCount = entryCount;
3376 if (!postcode.empty())
3377 {
3378 endCount = entryCount + postcode.size();
Ed Tanous3648c8b2022-07-25 13:39:59 -07003379 if (skip < endCount && (top + skip) > entryCount)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003380 {
Ed Tanous3648c8b2022-07-25 13:39:59 -07003381 uint64_t thisBootSkip =
3382 std::max(static_cast<uint64_t>(skip), entryCount) -
3383 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003384 uint64_t thisBootTop =
Ed Tanous3648c8b2022-07-25 13:39:59 -07003385 std::min(static_cast<uint64_t>(top + skip), endCount) -
3386 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003387
3388 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3389 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003390 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003391 aResp->res.jsonValue["Members@odata.count"] = endCount;
3392 }
3393
3394 // continue to previous bootIndex
3395 if (bootIndex < bootCount)
3396 {
3397 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3398 bootCount, endCount, skip, top);
3399 }
Jiaqing Zhao81584ab2022-07-28 00:33:45 +08003400 else if (skip + top < endCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07003401 {
3402 aResp->res.jsonValue["Members@odata.nextLink"] =
3403 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3404 std::to_string(skip + top);
3405 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003406 },
Jonathan Doman15124762021-01-07 17:54:17 -08003407 "xyz.openbmc_project.State.Boot.PostCode0",
3408 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003409 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3410 bootIndex);
3411}
3412
zhanghch058d1b46d2021-04-01 11:18:24 +08003413static void
3414 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003415 size_t skip, size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003416{
3417 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003418 sdbusplus::asio::getProperty<uint16_t>(
3419 *crow::connections::systemBus,
3420 "xyz.openbmc_project.State.Boot.PostCode0",
3421 "/xyz/openbmc_project/State/Boot/PostCode0",
3422 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3423 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3424 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003425 if (ec)
3426 {
3427 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3428 messages::internalError(aResp->res);
3429 return;
3430 }
3431 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003432 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003433}
3434
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003435inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003436{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003437 BMCWEB_ROUTE(app,
3438 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003439 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003440 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003441 [&app](const crow::Request& req,
3442 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003443 query_param::QueryCapabilities capabilities = {
3444 .canDelegateTop = true,
3445 .canDelegateSkip = true,
3446 };
3447 query_param::Query delegatedQuery;
3448 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003449 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003450 {
3451 return;
3452 }
3453 asyncResp->res.jsonValue["@odata.type"] =
3454 "#LogEntryCollection.LogEntryCollection";
3455 asyncResp->res.jsonValue["@odata.id"] =
3456 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3457 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3458 asyncResp->res.jsonValue["Description"] =
3459 "Collection of POST Code Log Entries";
3460 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3461 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07003462 size_t skip = delegatedQuery.skip.value_or(0);
3463 size_t top =
3464 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
3465 getCurrentBootNumber(asyncResp, skip, top);
Ed Tanous002d39b2022-05-31 08:59:27 -07003466 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003467}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003468
George Liu647b3cd2021-07-05 12:43:56 +08003469/**
3470 * @brief Parse post code ID and get the current value and index value
3471 * eg: postCodeID=B1-2, currentValue=1, index=2
3472 *
3473 * @param[in] postCodeID Post Code ID
3474 * @param[out] currentValue Current value
3475 * @param[out] index Index value
3476 *
3477 * @return bool true if the parsing is successful, false the parsing fails
3478 */
3479inline static bool parsePostCode(const std::string& postCodeID,
3480 uint64_t& currentValue, uint16_t& index)
3481{
3482 std::vector<std::string> split;
3483 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3484 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3485 {
3486 return false;
3487 }
3488
Ed Tanousca45aa32022-01-07 09:28:45 -08003489 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003490 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003491 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003492 const char* end = split[0].data() + split[0].size();
3493 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3494
3495 if (ptrIndex != end || ecIndex != std::errc())
3496 {
3497 return false;
3498 }
3499
3500 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003501
3502 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003503 end = split[1].data() + split[1].size();
3504 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003505
Tony Lee517d9a52022-06-28 15:41:23 +08003506 return ptrValue == end && ecValue == std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003507}
3508
3509inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3510{
George Liu0fda0f12021-11-16 10:06:17 +08003511 BMCWEB_ROUTE(
3512 app,
3513 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003514 .privileges(redfish::privileges::getLogEntry)
3515 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003516 [&app](const crow::Request& req,
3517 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3518 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003519 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003520 {
3521 return;
3522 }
3523 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3524 {
3525 asyncResp->res.result(boost::beast::http::status::bad_request);
3526 return;
3527 }
George Liu647b3cd2021-07-05 12:43:56 +08003528
Ed Tanous002d39b2022-05-31 08:59:27 -07003529 uint64_t currentValue = 0;
3530 uint16_t index = 0;
3531 if (!parsePostCode(postCodeID, currentValue, index))
3532 {
3533 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3534 return;
3535 }
George Liu647b3cd2021-07-05 12:43:56 +08003536
Ed Tanous002d39b2022-05-31 08:59:27 -07003537 crow::connections::systemBus->async_method_call(
3538 [asyncResp, postCodeID, currentValue](
3539 const boost::system::error_code ec,
3540 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3541 postcodes) {
3542 if (ec.value() == EBADR)
3543 {
3544 messages::resourceNotFound(asyncResp->res, "LogEntry",
3545 postCodeID);
3546 return;
3547 }
3548 if (ec)
3549 {
3550 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3551 messages::internalError(asyncResp->res);
3552 return;
3553 }
George Liu647b3cd2021-07-05 12:43:56 +08003554
Ed Tanous002d39b2022-05-31 08:59:27 -07003555 size_t value = static_cast<size_t>(currentValue) - 1;
3556 if (value == std::string::npos || postcodes.size() < currentValue)
3557 {
3558 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3559 messages::resourceNotFound(asyncResp->res, "LogEntry",
3560 postCodeID);
3561 return;
3562 }
George Liu647b3cd2021-07-05 12:43:56 +08003563
Ed Tanous002d39b2022-05-31 08:59:27 -07003564 const auto& [tID, c] = postcodes[value];
3565 if (c.empty())
3566 {
3567 BMCWEB_LOG_INFO << "No found post code data";
3568 messages::resourceNotFound(asyncResp->res, "LogEntry",
3569 postCodeID);
3570 return;
3571 }
3572 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3573 const char* d = reinterpret_cast<const char*>(c.data());
3574 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003575
Ed Tanous002d39b2022-05-31 08:59:27 -07003576 asyncResp->res.addHeader("Content-Type",
3577 "application/octet-stream");
3578 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
3579 asyncResp->res.body() = crow::utility::base64encode(strData);
3580 },
3581 "xyz.openbmc_project.State.Boot.PostCode0",
3582 "/xyz/openbmc_project/State/Boot/PostCode0",
3583 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3584 });
George Liu647b3cd2021-07-05 12:43:56 +08003585}
3586
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003587inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003588{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003589 BMCWEB_ROUTE(
3590 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003591 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003592 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003593 [&app](const crow::Request& req,
3594 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3595 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003596 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003597 {
3598 return;
3599 }
3600 uint16_t bootIndex = 0;
3601 uint64_t codeIndex = 0;
3602 if (!parsePostCode(targetID, codeIndex, bootIndex))
3603 {
3604 // Requested ID was not found
3605 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3606 return;
3607 }
3608 if (bootIndex == 0 || codeIndex == 0)
3609 {
3610 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3611 << targetID;
3612 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003613
Ed Tanous002d39b2022-05-31 08:59:27 -07003614 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3615 asyncResp->res.jsonValue["@odata.id"] =
3616 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3617 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3618 asyncResp->res.jsonValue["Description"] =
3619 "Collection of POST Code Log Entries";
3620 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3621 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003622
Ed Tanous002d39b2022-05-31 08:59:27 -07003623 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3624 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003625}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003626
Ed Tanous1da66f72018-07-27 16:13:37 -07003627} // namespace redfish