blob: 34e9f73cea91966eb0ae0a81c552bcdd157f5be0 [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 Tanous9896eae2022-07-23 15:07:33 -070030#include <boost/algorithm/string/case_conv.hpp>
Ed Tanous11ba3972022-07-11 09:50:41 -070031#include <boost/algorithm/string/classification.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060032#include <boost/algorithm/string/replace.hpp>
Jason M. Bills4851d452019-03-28 11:27:48 -070033#include <boost/algorithm/string/split.hpp>
Ed Tanous07c8c202022-07-11 10:08:08 -070034#include <boost/beast/http/verb.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070035#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080036#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080037#include <dbus_utility.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070038#include <error_messages.hpp>
Ed Tanous45ca1b82022-03-25 13:07:27 -070039#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070040#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050041
George Liu647b3cd2021-07-05 12:43:56 +080042#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070043#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080044#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070045#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070046#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080047#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070048
49namespace redfish
50{
51
Gunnar Mills1214b7e2020-06-04 10:11:30 -050052constexpr char const* crashdumpObject = "com.intel.crashdump";
53constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050054constexpr char const* crashdumpInterface = "com.intel.crashdump";
55constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070056 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050057constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070058 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070059constexpr char const* crashdumpTelemetryInterface =
60 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070061
Ed Tanousfffb8c12022-02-07 23:53:03 -080062namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -070063{
Ed Tanous26702d02021-11-03 15:02:33 -070064static const Message*
65 getMessageFromRegistry(const std::string& messageKey,
66 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070067{
Ed Tanous002d39b2022-05-31 08:59:27 -070068 std::span<const MessageEntry>::iterator messageIt =
69 std::find_if(registry.begin(), registry.end(),
70 [&messageKey](const MessageEntry& messageEntry) {
71 return std::strcmp(messageEntry.first, messageKey.c_str()) == 0;
Ed Tanous26702d02021-11-03 15:02:33 -070072 });
73 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070074 {
75 return &messageIt->second;
76 }
77
78 return nullptr;
79}
80
Gunnar Mills1214b7e2020-06-04 10:11:30 -050081static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070082{
83 // Redfish MessageIds are in the form
84 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
85 // the right Message
86 std::vector<std::string> fields;
87 fields.reserve(4);
88 boost::split(fields, messageID, boost::is_any_of("."));
Ed Tanous02cad962022-06-30 16:50:15 -070089 const std::string& registryName = fields[0];
90 const std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070091
92 // Find the right registry and check it for the MessageKey
93 if (std::string(base::header.registryPrefix) == registryName)
94 {
95 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070096 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070097 }
98 if (std::string(openbmc::header.registryPrefix) == registryName)
99 {
100 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -0700101 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -0700102 }
103 return nullptr;
104}
Ed Tanousfffb8c12022-02-07 23:53:03 -0800105} // namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -0700106
James Feistf6150402019-01-08 10:36:20 -0800107namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700108
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700110{
Ed Tanousd4d25792020-09-29 15:15:03 -0700111 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
112 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
113 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
114 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700115 {
116 return "Critical";
117 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700118 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
119 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
120 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700121 {
122 return "OK";
123 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700124 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700125 {
126 return "Warning";
127 }
128 return "";
129}
130
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700131inline static int getJournalMetadata(sd_journal* journal,
132 const std::string_view& field,
133 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700134{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500135 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700136 size_t length = 0;
137 int ret = 0;
138 // Get the metadata from the requested field of the journal entry
Ed Tanous46ff87b2022-01-07 09:25:51 -0800139 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
140 const void** dataVoid = reinterpret_cast<const void**>(&data);
141
142 ret = sd_journal_get_data(journal, field.data(), dataVoid, &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700143 if (ret < 0)
144 {
145 return ret;
146 }
Ed Tanous39e77502019-03-04 17:35:53 -0800147 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700148 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000149 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700150 return ret;
151}
152
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700153inline static int getJournalMetadata(sd_journal* journal,
154 const std::string_view& field,
155 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700156{
157 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800158 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700159 // Get the metadata from the requested field of the journal entry
160 ret = getJournalMetadata(journal, field, metadata);
161 if (ret < 0)
162 {
163 return ret;
164 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000165 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700166 return ret;
167}
168
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700169inline static bool getEntryTimestamp(sd_journal* journal,
170 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800171{
172 int ret = 0;
173 uint64_t timestamp = 0;
174 ret = sd_journal_get_realtime_usec(journal, &timestamp);
175 if (ret < 0)
176 {
177 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
178 << strerror(-ret);
179 return false;
180 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800181 entryTimestamp = crow::utility::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500182 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800183}
Ed Tanous50b8a432022-02-03 16:29:50 -0800184
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700185inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
186 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700187{
188 int ret = 0;
189 static uint64_t prevTs = 0;
190 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700191 if (firstEntry)
192 {
193 prevTs = 0;
194 }
195
Jason M. Bills16428a12018-11-02 12:42:29 -0700196 // Get the entry timestamp
197 uint64_t curTs = 0;
198 ret = sd_journal_get_realtime_usec(journal, &curTs);
199 if (ret < 0)
200 {
201 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
202 << strerror(-ret);
203 return false;
204 }
205 // If the timestamp isn't unique, increment the index
206 if (curTs == prevTs)
207 {
208 index++;
209 }
210 else
211 {
212 // Otherwise, reset it
213 index = 0;
214 }
215 // Save the timestamp
216 prevTs = curTs;
217
218 entryID = std::to_string(curTs);
219 if (index > 0)
220 {
221 entryID += "_" + std::to_string(index);
222 }
223 return true;
224}
225
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500226static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700227 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700228{
Ed Tanous271584a2019-07-09 16:24:22 -0700229 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700230 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700231 if (firstEntry)
232 {
233 prevTs = 0;
234 }
235
Jason M. Bills95820182019-04-22 16:25:34 -0700236 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700237 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700238 std::tm timeStruct = {};
239 std::istringstream entryStream(logEntry);
240 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
241 {
242 curTs = std::mktime(&timeStruct);
243 }
244 // If the timestamp isn't unique, increment the index
245 if (curTs == prevTs)
246 {
247 index++;
248 }
249 else
250 {
251 // Otherwise, reset it
252 index = 0;
253 }
254 // Save the timestamp
255 prevTs = curTs;
256
257 entryID = std::to_string(curTs);
258 if (index > 0)
259 {
260 entryID += "_" + std::to_string(index);
261 }
262 return true;
263}
264
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700265inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800266 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
267 const std::string& entryID, uint64_t& timestamp,
268 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700269{
270 if (entryID.empty())
271 {
272 return false;
273 }
274 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800275 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700276
Ed Tanous81ce6092020-12-17 16:54:55 +0000277 auto underscorePos = tsStr.find('_');
Ed Tanous71d5d8d2022-01-25 11:04:33 -0800278 if (underscorePos != std::string_view::npos)
Jason M. Bills16428a12018-11-02 12:42:29 -0700279 {
280 // Timestamp has an index
281 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800282 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700283 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700284 auto [ptr, ec] = std::from_chars(
285 indexStr.data(), indexStr.data() + indexStr.size(), index);
286 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700287 {
Ed Tanousace85d62021-10-26 12:45:59 -0700288 messages::resourceMissingAtURI(
289 asyncResp->res, crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700290 return false;
291 }
292 }
293 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700294 auto [ptr, ec] =
295 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
296 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700297 {
Ed Tanousace85d62021-10-26 12:45:59 -0700298 messages::resourceMissingAtURI(asyncResp->res,
299 crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700300 return false;
301 }
302 return true;
303}
304
Jason M. Bills95820182019-04-22 16:25:34 -0700305static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500306 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700307{
308 static const std::filesystem::path redfishLogDir = "/var/log";
309 static const std::string redfishLogFilename = "redfish";
310
311 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500312 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700313 std::filesystem::directory_iterator(redfishLogDir))
314 {
315 // If we find a redfish log file, save the path
316 std::string filename = dirEnt.path().filename();
Ed Tanous11ba3972022-07-11 09:50:41 -0700317 if (filename.starts_with(redfishLogFilename))
Jason M. Bills95820182019-04-22 16:25:34 -0700318 {
319 redfishLogFiles.emplace_back(redfishLogDir / filename);
320 }
321 }
322 // As the log files rotate, they are appended with a ".#" that is higher for
323 // the older logs. Since we don't expect more than 10 log files, we
324 // can just sort the list to get them in order from newest to oldest
325 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
326
327 return !redfishLogFiles.empty();
328}
329
Claire Weinanaefe3782022-07-15 19:17:19 -0700330inline void parseDumpEntryFromDbusObject(
331 const dbus::utility::ManagedItem& object, std::string& dumpStatus,
332 uint64_t& size, uint64_t& timestamp,
333 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
334{
335 for (const auto& interfaceMap : object.second)
336 {
337 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
338 {
339 for (const auto& propertyMap : interfaceMap.second)
340 {
341 if (propertyMap.first == "Status")
342 {
343 const auto* status =
344 std::get_if<std::string>(&propertyMap.second);
345 if (status == nullptr)
346 {
347 messages::internalError(asyncResp->res);
348 break;
349 }
350 dumpStatus = *status;
351 }
352 }
353 }
354 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
355 {
356 for (const auto& propertyMap : interfaceMap.second)
357 {
358 if (propertyMap.first == "Size")
359 {
360 const auto* sizePtr =
361 std::get_if<uint64_t>(&propertyMap.second);
362 if (sizePtr == nullptr)
363 {
364 messages::internalError(asyncResp->res);
365 break;
366 }
367 size = *sizePtr;
368 break;
369 }
370 }
371 }
372 else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime")
373 {
374 for (const auto& propertyMap : interfaceMap.second)
375 {
376 if (propertyMap.first == "Elapsed")
377 {
378 const uint64_t* usecsTimeStamp =
379 std::get_if<uint64_t>(&propertyMap.second);
380 if (usecsTimeStamp == nullptr)
381 {
382 messages::internalError(asyncResp->res);
383 break;
384 }
385 timestamp = *usecsTimeStamp;
386 break;
387 }
388 }
389 }
390 }
391}
392
Nan Zhou21ab4042022-06-26 23:07:40 +0000393static std::string getDumpEntriesPath(const std::string& dumpType)
Claire Weinanfdd26902022-03-01 14:18:25 -0800394{
395 std::string entriesPath;
396
397 if (dumpType == "BMC")
398 {
399 entriesPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
400 }
401 else if (dumpType == "FaultLog")
402 {
403 entriesPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/";
404 }
405 else if (dumpType == "System")
406 {
407 entriesPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
408 }
409 else
410 {
411 BMCWEB_LOG_ERROR << "getDumpEntriesPath() invalid dump type: "
412 << dumpType;
413 }
414
415 // Returns empty string on error
416 return entriesPath;
417}
418
zhanghch058d1b46d2021-04-01 11:18:24 +0800419inline void
420 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
421 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500422{
Claire Weinanfdd26902022-03-01 14:18:25 -0800423 std::string entriesPath = getDumpEntriesPath(dumpType);
424 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500425 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500426 messages::internalError(asyncResp->res);
427 return;
428 }
429
430 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800431 [asyncResp, entriesPath,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800432 dumpType](const boost::system::error_code ec,
433 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700434 if (ec)
435 {
436 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
437 messages::internalError(asyncResp->res);
438 return;
439 }
440
Claire Weinanfdd26902022-03-01 14:18:25 -0800441 // Remove ending slash
442 std::string odataIdStr = entriesPath;
443 if (!odataIdStr.empty())
444 {
445 odataIdStr.pop_back();
446 }
447
448 asyncResp->res.jsonValue["@odata.type"] =
449 "#LogEntryCollection.LogEntryCollection";
450 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
451 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
452 asyncResp->res.jsonValue["Description"] =
453 "Collection of " + dumpType + " Dump Entries";
454
Ed Tanous002d39b2022-05-31 08:59:27 -0700455 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
456 entriesArray = nlohmann::json::array();
457 std::string dumpEntryPath =
458 "/xyz/openbmc_project/dump/" +
459 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
460
461 std::sort(resp.begin(), resp.end(), [](const auto& l, const auto& r) {
462 return AlphanumLess<std::string>()(l.first.filename(),
463 r.first.filename());
464 });
465
466 for (auto& object : resp)
467 {
468 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500469 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700470 continue;
471 }
472 uint64_t timestamp = 0;
473 uint64_t size = 0;
474 std::string dumpStatus;
Jason M. Bills433b68b2022-06-28 12:24:26 -0700475 nlohmann::json::object_t thisEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -0700476
477 std::string entryID = object.first.filename();
478 if (entryID.empty())
479 {
480 continue;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500481 }
482
Claire Weinanaefe3782022-07-15 19:17:19 -0700483 parseDumpEntryFromDbusObject(object, dumpStatus, size, timestamp,
484 asyncResp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700485
486 if (dumpStatus !=
487 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
488 !dumpStatus.empty())
489 {
490 // Dump status is not Complete, no need to enumerate
491 continue;
492 }
493
494 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800495 thisEntry["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700496 thisEntry["Id"] = entryID;
497 thisEntry["EntryType"] = "Event";
498 thisEntry["Created"] = crow::utility::getDateTimeUint(timestamp);
499 thisEntry["Name"] = dumpType + " Dump Entry";
500
Ed Tanous002d39b2022-05-31 08:59:27 -0700501 if (dumpType == "BMC")
502 {
503 thisEntry["DiagnosticDataType"] = "Manager";
504 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800505 entriesPath + entryID + "/attachment";
506 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700507 }
508 else if (dumpType == "System")
509 {
510 thisEntry["DiagnosticDataType"] = "OEM";
511 thisEntry["OEMDiagnosticDataType"] = "System";
512 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800513 entriesPath + entryID + "/attachment";
514 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700515 }
516 entriesArray.push_back(std::move(thisEntry));
517 }
518 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500519 },
520 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
521 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
522}
523
zhanghch058d1b46d2021-04-01 11:18:24 +0800524inline void
Claire Weinanc7a6d662022-06-13 16:36:39 -0700525 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800526 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500527{
Claire Weinanfdd26902022-03-01 14:18:25 -0800528 std::string entriesPath = getDumpEntriesPath(dumpType);
529 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500530 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500531 messages::internalError(asyncResp->res);
532 return;
533 }
534
535 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800536 [asyncResp, entryID, dumpType,
537 entriesPath](const boost::system::error_code ec,
Ed Tanous02cad962022-06-30 16:50:15 -0700538 const dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700539 if (ec)
540 {
541 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
542 messages::internalError(asyncResp->res);
543 return;
544 }
545
546 bool foundDumpEntry = false;
547 std::string dumpEntryPath =
548 "/xyz/openbmc_project/dump/" +
549 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
550
551 for (const auto& objectPath : resp)
552 {
553 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500554 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700555 continue;
556 }
557
558 foundDumpEntry = true;
559 uint64_t timestamp = 0;
560 uint64_t size = 0;
561 std::string dumpStatus;
562
Claire Weinanaefe3782022-07-15 19:17:19 -0700563 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size,
564 timestamp, asyncResp);
Ed Tanous002d39b2022-05-31 08:59:27 -0700565
566 if (dumpStatus !=
567 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
568 !dumpStatus.empty())
569 {
570 // Dump status is not Complete
571 // return not found until status is changed to Completed
572 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
573 entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500574 return;
575 }
576
Ed Tanous002d39b2022-05-31 08:59:27 -0700577 asyncResp->res.jsonValue["@odata.type"] =
578 "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800579 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700580 asyncResp->res.jsonValue["Id"] = entryID;
581 asyncResp->res.jsonValue["EntryType"] = "Event";
582 asyncResp->res.jsonValue["Created"] =
583 crow::utility::getDateTimeUint(timestamp);
584 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500585
Ed Tanous002d39b2022-05-31 08:59:27 -0700586 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500587 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700588 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
589 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800590 entriesPath + entryID + "/attachment";
591 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500592 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700593 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500594 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700595 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
596 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
597 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800598 entriesPath + entryID + "/attachment";
599 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500600 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700601 }
602 if (!foundDumpEntry)
603 {
604 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
605 messages::internalError(asyncResp->res);
606 return;
607 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500608 },
609 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
610 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
611}
612
zhanghch058d1b46d2021-04-01 11:18:24 +0800613inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800614 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500615 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500616{
Ed Tanous002d39b2022-05-31 08:59:27 -0700617 auto respHandler =
618 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500619 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
620 if (ec)
621 {
George Liu3de8d8b2021-03-22 17:49:39 +0800622 if (ec.value() == EBADR)
623 {
624 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
625 return;
626 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500627 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -0800628 << ec << " entryID=" << entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500629 messages::internalError(asyncResp->res);
630 return;
631 }
632 };
633 crow::connections::systemBus->async_method_call(
634 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500635 "/xyz/openbmc_project/dump/" +
636 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
637 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500638 "xyz.openbmc_project.Object.Delete", "Delete");
639}
640
zhanghch058d1b46d2021-04-01 11:18:24 +0800641inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700642 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800643 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
644 const uint32_t& dumpId, const std::string& dumpPath,
645 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500646{
647 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -0500648 [dumpId, dumpPath,
649 dumpType](boost::system::error_code err, sdbusplus::message_t& m,
650 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700651 if (err)
652 {
653 BMCWEB_LOG_ERROR << "Error in creating a dump";
654 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500655 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700656 }
657
658 dbus::utility::DBusInteracesMap interfacesList;
659
660 sdbusplus::message::object_path objPath;
661
662 m.read(objPath, interfacesList);
663
664 if (objPath.str ==
665 "/xyz/openbmc_project/dump/" +
666 std::string(boost::algorithm::to_lower_copy(dumpType)) +
667 "/entry/" + std::to_string(dumpId))
668 {
669 nlohmann::json retMessage = messages::success();
670 taskData->messages.emplace_back(retMessage);
671
672 std::string headerLoc =
673 "Location: " + dumpPath + std::to_string(dumpId);
674 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
675
676 taskData->state = "Completed";
677 return task::completed;
678 }
679 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500680 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800681 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500682 "member='InterfacesAdded', "
683 "path='/xyz/openbmc_project/dump'");
684
685 task->startTimer(std::chrono::minutes(3));
686 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700687 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500688}
689
zhanghch058d1b46d2021-04-01 11:18:24 +0800690inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
691 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500692{
Claire Weinanfdd26902022-03-01 14:18:25 -0800693 std::string dumpPath = getDumpEntriesPath(dumpType);
694 if (dumpPath.empty())
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500695 {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500696 messages::internalError(asyncResp->res);
697 return;
698 }
699
700 std::optional<std::string> diagnosticDataType;
701 std::optional<std::string> oemDiagnosticDataType;
702
Willy Tu15ed6782021-12-14 11:03:16 -0800703 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500704 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
705 "OEMDiagnosticDataType", oemDiagnosticDataType))
706 {
707 return;
708 }
709
710 if (dumpType == "System")
711 {
712 if (!oemDiagnosticDataType || !diagnosticDataType)
713 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800714 BMCWEB_LOG_ERROR
715 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500716 messages::actionParameterMissing(
717 asyncResp->res, "CollectDiagnosticData",
718 "DiagnosticDataType & OEMDiagnosticDataType");
719 return;
720 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700721 if ((*oemDiagnosticDataType != "System") ||
722 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500723 {
724 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700725 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500726 return;
727 }
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500728 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500729 }
730 else if (dumpType == "BMC")
731 {
732 if (!diagnosticDataType)
733 {
George Liu0fda0f12021-11-16 10:06:17 +0800734 BMCWEB_LOG_ERROR
735 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500736 messages::actionParameterMissing(
737 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
738 return;
739 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700740 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500741 {
742 BMCWEB_LOG_ERROR
743 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700744 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500745 return;
746 }
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500747 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/";
748 }
749 else
750 {
751 BMCWEB_LOG_ERROR << "CreateDump failed. Unknown dump type";
752 messages::internalError(asyncResp->res);
753 return;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500754 }
755
756 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700757 [asyncResp, payload(task::Payload(req)), dumpPath,
758 dumpType](const boost::system::error_code ec,
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500759 const sdbusplus::message::message& msg,
Ed Tanous98be3e32021-09-16 15:05:36 -0700760 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700761 if (ec)
762 {
763 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
Asmitha Karunanithi59075712021-10-22 01:17:41 -0500764 const sd_bus_error* dbusError = msg.get_error();
765 if (dbusError == nullptr)
766 {
767 messages::internalError(asyncResp->res);
768 return;
769 }
770
771 BMCWEB_LOG_ERROR << "CreateDump DBus error: " << dbusError->name
772 << " and error msg: " << dbusError->message;
773 if (std::string_view(
774 "xyz.openbmc_project.Common.Error.NotAllowed") ==
775 dbusError->name)
776 {
777 messages::resourceInStandby(asyncResp->res);
778 return;
779 }
780 if (std::string_view(
781 "xyz.openbmc_project.Dump.Create.Error.Disabled") ==
782 dbusError->name)
783 {
784 messages::serviceDisabled(asyncResp->res, dumpPath);
785 return;
786 }
787 if (std::string_view(
788 "xyz.openbmc_project.Common.Error.Unavailable") ==
789 dbusError->name)
790 {
791 messages::resourceInUse(asyncResp->res);
792 return;
793 }
794 // Other Dbus errors such as:
795 // xyz.openbmc_project.Common.Error.InvalidArgument &
796 // org.freedesktop.DBus.Error.InvalidArgs are all related to
797 // the dbus call that is made here in the bmcweb
798 // implementation and has nothing to do with the client's
799 // input in the request. Hence, returning internal error
800 // back to the client.
Ed Tanous002d39b2022-05-31 08:59:27 -0700801 messages::internalError(asyncResp->res);
802 return;
803 }
804 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500805
Ed Tanous002d39b2022-05-31 08:59:27 -0700806 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
807 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500808 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500809 "xyz.openbmc_project.Dump.Manager",
810 "/xyz/openbmc_project/dump/" +
811 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500812 "xyz.openbmc_project.Dump.Create", "CreateDump");
813}
814
zhanghch058d1b46d2021-04-01 11:18:24 +0800815inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
816 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500817{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500818 std::string dumpTypeLowerCopy =
819 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800820
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500821 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800822 [asyncResp, dumpType](
823 const boost::system::error_code ec,
824 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700825 if (ec)
826 {
827 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
828 messages::internalError(asyncResp->res);
829 return;
830 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500831
Ed Tanous002d39b2022-05-31 08:59:27 -0700832 for (const std::string& path : subTreePaths)
833 {
834 sdbusplus::message::object_path objPath(path);
835 std::string logID = objPath.filename();
836 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500837 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700838 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500839 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700840 deleteDumpEntry(asyncResp, logID, dumpType);
841 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500842 },
843 "xyz.openbmc_project.ObjectMapper",
844 "/xyz/openbmc_project/object_mapper",
845 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500846 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
847 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
848 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500849}
850
Ed Tanousb9d36b42022-02-26 21:42:46 -0800851inline static void
852 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
853 std::string& filename, std::string& timestamp,
854 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700855{
856 for (auto property : params)
857 {
858 if (property.first == "Timestamp")
859 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500860 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500861 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700862 if (value != nullptr)
863 {
864 timestamp = *value;
865 }
866 }
867 else if (property.first == "Filename")
868 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500869 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500870 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700871 if (value != nullptr)
872 {
873 filename = *value;
874 }
875 }
876 else if (property.first == "Log")
877 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500878 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500879 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700880 if (value != nullptr)
881 {
882 logfile = *value;
883 }
884 }
885 }
886}
887
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500888constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700889inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700890{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800891 /**
892 * Functions triggers appropriate requests on DBus
893 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700894 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700895 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700896 .methods(boost::beast::http::verb::get)(
897 [&app](const crow::Request& req,
898 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000899 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700900 {
901 return;
902 }
903 // Collections don't include the static data added by SubRoute
904 // because it has a duplicate entry for members
905 asyncResp->res.jsonValue["@odata.type"] =
906 "#LogServiceCollection.LogServiceCollection";
907 asyncResp->res.jsonValue["@odata.id"] =
908 "/redfish/v1/Systems/system/LogServices";
909 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
910 asyncResp->res.jsonValue["Description"] =
911 "Collection of LogServices for this Computer System";
912 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
913 logServiceArray = nlohmann::json::array();
914 nlohmann::json::object_t eventLog;
915 eventLog["@odata.id"] =
916 "/redfish/v1/Systems/system/LogServices/EventLog";
917 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500918#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700919 nlohmann::json::object_t dumpLog;
920 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
921 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600922#endif
923
Jason M. Billsd53dd412019-02-12 17:16:22 -0800924#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700925 nlohmann::json::object_t crashdump;
926 crashdump["@odata.id"] =
927 "/redfish/v1/Systems/system/LogServices/Crashdump";
928 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800929#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800930
931#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700932 nlohmann::json::object_t hostlogger;
933 hostlogger["@odata.id"] =
934 "/redfish/v1/Systems/system/LogServices/HostLogger";
935 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800936#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700937 asyncResp->res.jsonValue["Members@odata.count"] =
938 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800939
Ed Tanous002d39b2022-05-31 08:59:27 -0700940 crow::connections::systemBus->async_method_call(
941 [asyncResp](const boost::system::error_code ec,
942 const dbus::utility::MapperGetSubTreePathsResponse&
943 subtreePath) {
944 if (ec)
945 {
946 BMCWEB_LOG_ERROR << ec;
947 return;
948 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700949
Ed Tanous002d39b2022-05-31 08:59:27 -0700950 for (const auto& pathStr : subtreePath)
951 {
952 if (pathStr.find("PostCode") != std::string::npos)
953 {
954 nlohmann::json& logServiceArrayLocal =
955 asyncResp->res.jsonValue["Members"];
956 logServiceArrayLocal.push_back(
957 {{"@odata.id",
958 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
959 asyncResp->res.jsonValue["Members@odata.count"] =
960 logServiceArrayLocal.size();
961 return;
962 }
963 }
964 },
965 "xyz.openbmc_project.ObjectMapper",
966 "/xyz/openbmc_project/object_mapper",
967 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
968 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700969 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700970}
971
972inline void requestRoutesEventLogService(App& app)
973{
974 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700975 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700976 .methods(boost::beast::http::verb::get)(
977 [&app](const crow::Request& req,
978 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000979 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700980 {
981 return;
982 }
983 asyncResp->res.jsonValue["@odata.id"] =
984 "/redfish/v1/Systems/system/LogServices/EventLog";
985 asyncResp->res.jsonValue["@odata.type"] =
986 "#LogService.v1_1_0.LogService";
987 asyncResp->res.jsonValue["Name"] = "Event Log Service";
988 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
989 asyncResp->res.jsonValue["Id"] = "EventLog";
990 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530991
Ed Tanous002d39b2022-05-31 08:59:27 -0700992 std::pair<std::string, std::string> redfishDateTimeOffset =
993 crow::utility::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530994
Ed Tanous002d39b2022-05-31 08:59:27 -0700995 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
996 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
997 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +0530998
Ed Tanous002d39b2022-05-31 08:59:27 -0700999 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1000 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1001 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001002
Ed Tanous002d39b2022-05-31 08:59:27 -07001003 {"target",
1004 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001005 });
1006}
1007
1008inline void requestRoutesJournalEventLogClear(App& app)
1009{
Jason M. Bills4978b632022-02-22 14:17:43 -08001010 BMCWEB_ROUTE(
1011 app,
1012 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001013 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001014 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001015 [&app](const crow::Request& req,
1016 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001017 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001018 {
1019 return;
1020 }
1021 // Clear the EventLog by deleting the log files
1022 std::vector<std::filesystem::path> redfishLogFiles;
1023 if (getRedfishLogFiles(redfishLogFiles))
1024 {
1025 for (const std::filesystem::path& file : redfishLogFiles)
1026 {
1027 std::error_code ec;
1028 std::filesystem::remove(file, ec);
1029 }
1030 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001031
Ed Tanous002d39b2022-05-31 08:59:27 -07001032 // Reload rsyslog so it knows to start new log files
1033 crow::connections::systemBus->async_method_call(
1034 [asyncResp](const boost::system::error_code ec) {
1035 if (ec)
1036 {
1037 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1038 messages::internalError(asyncResp->res);
1039 return;
1040 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001041
Ed Tanous002d39b2022-05-31 08:59:27 -07001042 messages::success(asyncResp->res);
1043 },
1044 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1045 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1046 "replace");
1047 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001048}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001049
Jason M. Billsac992cd2022-06-24 13:31:46 -07001050enum class LogParseError
1051{
1052 success,
1053 parseFailed,
1054 messageIdNotInRegistry,
1055};
1056
1057static LogParseError
1058 fillEventLogEntryJson(const std::string& logEntryID,
1059 const std::string& logEntry,
1060 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001061{
Jason M. Bills95820182019-04-22 16:25:34 -07001062 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001063 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001064 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001065 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001066 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001067 return LogParseError::parseFailed;
Jason M. Bills95820182019-04-22 16:25:34 -07001068 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001069 std::string timestamp = logEntry.substr(0, space);
1070 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001071 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001072 if (entryStart == std::string::npos)
1073 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001074 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001075 }
1076 std::string_view entry(logEntry);
1077 entry.remove_prefix(entryStart);
1078 // Use split to separate the entry into its fields
1079 std::vector<std::string> logEntryFields;
1080 boost::split(logEntryFields, entry, boost::is_any_of(","),
1081 boost::token_compress_on);
1082 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001083 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001084 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001085 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001086 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001087 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001088
Jason M. Bills4851d452019-03-28 11:27:48 -07001089 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001090 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001091
Sui Chen54417b02022-03-24 14:59:52 -07001092 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001093 {
Sui Chen54417b02022-03-24 14:59:52 -07001094 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001095 return LogParseError::messageIdNotInRegistry;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001096 }
1097
Sui Chen54417b02022-03-24 14:59:52 -07001098 std::string msg = message->message;
1099
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001100 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001101 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001102 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001103 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001104 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001105 // If the first string is empty, assume there are no MessageArgs
1106 std::size_t messageArgsSize = 0;
1107 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001108 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001109 messageArgsSize = logEntryFields.size() - 1;
1110 }
1111
Ed Tanous23a21a12020-07-25 04:45:05 +00001112 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001113
1114 // Fill the MessageArgs into the Message
1115 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001116 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001117 {
1118 std::string argStr = "%" + std::to_string(++i);
1119 size_t argPos = msg.find(argStr);
1120 if (argPos != std::string::npos)
1121 {
1122 msg.replace(argPos, argStr.length(), messageArg);
1123 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001124 }
1125 }
1126
Jason M. Bills95820182019-04-22 16:25:34 -07001127 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1128 // format which matches the Redfish format except for the fractional seconds
1129 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001130 std::size_t dot = timestamp.find_first_of('.');
1131 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001132 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001133 {
Jason M. Bills95820182019-04-22 16:25:34 -07001134 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001135 }
1136
1137 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07001138 logEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1139 logEntryJson["@odata.id"] =
1140 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1141 logEntryJson["Name"] = "System Event Log Entry";
1142 logEntryJson["Id"] = logEntryID;
1143 logEntryJson["Message"] = std::move(msg);
1144 logEntryJson["MessageId"] = std::move(messageID);
1145 logEntryJson["MessageArgs"] = messageArgs;
1146 logEntryJson["EntryType"] = "Event";
1147 logEntryJson["Severity"] = message->messageSeverity;
1148 logEntryJson["Created"] = std::move(timestamp);
Jason M. Billsac992cd2022-06-24 13:31:46 -07001149 return LogParseError::success;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001150}
1151
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001152inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001153{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001154 BMCWEB_ROUTE(app,
1155 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001156 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001157 .methods(boost::beast::http::verb::get)(
1158 [&app](const crow::Request& req,
1159 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1160 query_param::QueryCapabilities capabilities = {
1161 .canDelegateTop = true,
1162 .canDelegateSkip = true,
1163 };
1164 query_param::Query delegatedQuery;
1165 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001166 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001167 {
1168 return;
1169 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001170 size_t top =
1171 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
1172 size_t skip = delegatedQuery.skip.value_or(0);
1173
Ed Tanous002d39b2022-05-31 08:59:27 -07001174 // Collections don't include the static data added by SubRoute
1175 // because it has a duplicate entry for members
1176 asyncResp->res.jsonValue["@odata.type"] =
1177 "#LogEntryCollection.LogEntryCollection";
1178 asyncResp->res.jsonValue["@odata.id"] =
1179 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1180 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1181 asyncResp->res.jsonValue["Description"] =
1182 "Collection of System Event Log Entries";
1183
1184 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1185 logEntryArray = nlohmann::json::array();
1186 // Go through the log files and create a unique ID for each
1187 // entry
1188 std::vector<std::filesystem::path> redfishLogFiles;
1189 getRedfishLogFiles(redfishLogFiles);
1190 uint64_t entryCount = 0;
1191 std::string logEntry;
1192
1193 // Oldest logs are in the last file, so start there and loop
1194 // backwards
1195 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1196 it++)
1197 {
1198 std::ifstream logStream(*it);
1199 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001200 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001201 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001202 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001203
Ed Tanous002d39b2022-05-31 08:59:27 -07001204 // Reset the unique ID on the first entry
1205 bool firstEntry = true;
1206 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001207 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001208 std::string idStr;
1209 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001210 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001211 continue;
1212 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001213 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001214
Jason M. Billsde703c52022-06-23 14:19:04 -07001215 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001216 LogParseError status =
1217 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1218 if (status == LogParseError::messageIdNotInRegistry)
1219 {
1220 continue;
1221 }
1222 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001223 {
1224 messages::internalError(asyncResp->res);
1225 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001226 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001227
Jason M. Billsde703c52022-06-23 14:19:04 -07001228 entryCount++;
1229 // Handle paging using skip (number of entries to skip from the
1230 // start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07001231 if (entryCount <= skip || entryCount > skip + top)
Jason M. Billsde703c52022-06-23 14:19:04 -07001232 {
1233 continue;
1234 }
1235
1236 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001237 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001238 }
1239 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001240 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07001241 {
1242 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1243 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001244 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07001245 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001246 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001247}
Chicago Duan336e96c2019-07-15 14:22:08 +08001248
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001249inline void requestRoutesJournalEventLogEntry(App& app)
1250{
1251 BMCWEB_ROUTE(
1252 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001253 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001254 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001255 [&app](const crow::Request& req,
1256 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1257 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001258 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001259 {
1260 return;
1261 }
1262 const std::string& targetID = param;
1263
1264 // Go through the log files and check the unique ID for each
1265 // entry to find the target entry
1266 std::vector<std::filesystem::path> redfishLogFiles;
1267 getRedfishLogFiles(redfishLogFiles);
1268 std::string logEntry;
1269
1270 // Oldest logs are in the last file, so start there and loop
1271 // backwards
1272 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1273 it++)
1274 {
1275 std::ifstream logStream(*it);
1276 if (!logStream.is_open())
1277 {
1278 continue;
1279 }
1280
1281 // Reset the unique ID on the first entry
1282 bool firstEntry = true;
1283 while (std::getline(logStream, logEntry))
1284 {
1285 std::string idStr;
1286 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001287 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001288 continue;
1289 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001290 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001291
1292 if (idStr == targetID)
1293 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001294 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001295 LogParseError status =
1296 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1297 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001298 {
1299 messages::internalError(asyncResp->res);
1300 return;
1301 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07001302 asyncResp->res.jsonValue.update(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001303 return;
1304 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001305 }
1306 }
1307 // Requested ID was not found
1308 messages::resourceMissingAtURI(asyncResp->res,
1309 crow::utility::urlFromPieces(targetID));
1310 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001311}
1312
1313inline void requestRoutesDBusEventLogEntryCollection(App& app)
1314{
1315 BMCWEB_ROUTE(app,
1316 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001317 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001318 .methods(boost::beast::http::verb::get)(
1319 [&app](const crow::Request& req,
1320 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001321 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001322 {
1323 return;
1324 }
1325 // Collections don't include the static data added by SubRoute
1326 // because it has a duplicate entry for members
1327 asyncResp->res.jsonValue["@odata.type"] =
1328 "#LogEntryCollection.LogEntryCollection";
1329 asyncResp->res.jsonValue["@odata.id"] =
1330 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1331 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1332 asyncResp->res.jsonValue["Description"] =
1333 "Collection of System Event Log Entries";
1334
1335 // DBus implementation of EventLog/Entries
1336 // Make call to Logging Service to find all log entry objects
1337 crow::connections::systemBus->async_method_call(
1338 [asyncResp](const boost::system::error_code ec,
1339 const dbus::utility::ManagedObjectType& resp) {
1340 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001341 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001342 // TODO Handle for specific error code
1343 BMCWEB_LOG_ERROR
1344 << "getLogEntriesIfaceData resp_handler got error " << ec;
1345 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001346 return;
1347 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001348 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1349 entriesArray = nlohmann::json::array();
1350 for (const auto& objectPath : resp)
1351 {
1352 const uint32_t* id = nullptr;
1353 const uint64_t* timestamp = nullptr;
1354 const uint64_t* updateTimestamp = nullptr;
1355 const std::string* severity = nullptr;
1356 const std::string* message = nullptr;
1357 const std::string* filePath = nullptr;
1358 bool resolved = false;
1359 for (const auto& interfaceMap : objectPath.second)
1360 {
1361 if (interfaceMap.first ==
1362 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001363 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001364 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001365 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001366 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001367 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001368 id = std::get_if<uint32_t>(&propertyMap.second);
1369 }
1370 else if (propertyMap.first == "Timestamp")
1371 {
1372 timestamp =
1373 std::get_if<uint64_t>(&propertyMap.second);
1374 }
1375 else if (propertyMap.first == "UpdateTimestamp")
1376 {
1377 updateTimestamp =
1378 std::get_if<uint64_t>(&propertyMap.second);
1379 }
1380 else if (propertyMap.first == "Severity")
1381 {
1382 severity = std::get_if<std::string>(
1383 &propertyMap.second);
1384 }
1385 else if (propertyMap.first == "Message")
1386 {
1387 message = std::get_if<std::string>(
1388 &propertyMap.second);
1389 }
1390 else if (propertyMap.first == "Resolved")
1391 {
1392 const bool* resolveptr =
1393 std::get_if<bool>(&propertyMap.second);
1394 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001395 {
1396 messages::internalError(asyncResp->res);
1397 return;
1398 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001399 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001400 }
1401 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001402 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001403 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001404 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001405 messages::internalError(asyncResp->res);
1406 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001407 }
1408 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001409 else if (interfaceMap.first ==
1410 "xyz.openbmc_project.Common.FilePath")
1411 {
1412 for (const auto& propertyMap : interfaceMap.second)
1413 {
1414 if (propertyMap.first == "Path")
1415 {
1416 filePath = std::get_if<std::string>(
1417 &propertyMap.second);
1418 }
1419 }
1420 }
1421 }
1422 // Object path without the
1423 // xyz.openbmc_project.Logging.Entry interface, ignore
1424 // and continue.
1425 if (id == nullptr || message == nullptr ||
1426 severity == nullptr || timestamp == nullptr ||
1427 updateTimestamp == nullptr)
1428 {
1429 continue;
1430 }
1431 entriesArray.push_back({});
1432 nlohmann::json& thisEntry = entriesArray.back();
1433 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1434 thisEntry["@odata.id"] =
1435 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1436 std::to_string(*id);
1437 thisEntry["Name"] = "System Event Log Entry";
1438 thisEntry["Id"] = std::to_string(*id);
1439 thisEntry["Message"] = *message;
1440 thisEntry["Resolved"] = resolved;
1441 thisEntry["EntryType"] = "Event";
1442 thisEntry["Severity"] =
1443 translateSeverityDbusToRedfish(*severity);
1444 thisEntry["Created"] =
1445 crow::utility::getDateTimeUintMs(*timestamp);
1446 thisEntry["Modified"] =
1447 crow::utility::getDateTimeUintMs(*updateTimestamp);
1448 if (filePath != nullptr)
1449 {
1450 thisEntry["AdditionalDataURI"] =
1451 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1452 std::to_string(*id) + "/attachment";
1453 }
1454 }
1455 std::sort(
1456 entriesArray.begin(), entriesArray.end(),
1457 [](const nlohmann::json& left, const nlohmann::json& right) {
1458 return (left["Id"] <= right["Id"]);
1459 });
1460 asyncResp->res.jsonValue["Members@odata.count"] =
1461 entriesArray.size();
1462 },
1463 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1464 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001465 });
1466}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001467
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001468inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001469{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001470 BMCWEB_ROUTE(
1471 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001472 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001473 .methods(boost::beast::http::verb::get)(
1474 [&app](const crow::Request& req,
1475 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1476 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001477 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001478 {
1479 return;
1480 }
1481 std::string entryID = param;
1482 dbus::utility::escapePathForDbus(entryID);
1483
1484 // DBus implementation of EventLog/Entries
1485 // Make call to Logging Service to find all log entry objects
1486 crow::connections::systemBus->async_method_call(
1487 [asyncResp, entryID](const boost::system::error_code ec,
1488 const dbus::utility::DBusPropertiesMap& resp) {
1489 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001490 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001491 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1492 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001493 return;
1494 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001495 if (ec)
1496 {
1497 BMCWEB_LOG_ERROR
1498 << "EventLogEntry (DBus) resp_handler got error " << ec;
1499 messages::internalError(asyncResp->res);
1500 return;
1501 }
1502 const uint32_t* id = nullptr;
1503 const uint64_t* timestamp = nullptr;
1504 const uint64_t* updateTimestamp = nullptr;
1505 const std::string* severity = nullptr;
1506 const std::string* message = nullptr;
1507 const std::string* filePath = nullptr;
1508 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001509
Ed Tanous002d39b2022-05-31 08:59:27 -07001510 for (const auto& propertyMap : resp)
1511 {
1512 if (propertyMap.first == "Id")
1513 {
1514 id = std::get_if<uint32_t>(&propertyMap.second);
1515 }
1516 else if (propertyMap.first == "Timestamp")
1517 {
1518 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1519 }
1520 else if (propertyMap.first == "UpdateTimestamp")
1521 {
1522 updateTimestamp =
1523 std::get_if<uint64_t>(&propertyMap.second);
1524 }
1525 else if (propertyMap.first == "Severity")
1526 {
1527 severity = std::get_if<std::string>(&propertyMap.second);
1528 }
1529 else if (propertyMap.first == "Message")
1530 {
1531 message = std::get_if<std::string>(&propertyMap.second);
1532 }
1533 else if (propertyMap.first == "Resolved")
1534 {
1535 const bool* resolveptr =
1536 std::get_if<bool>(&propertyMap.second);
1537 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001538 {
1539 messages::internalError(asyncResp->res);
1540 return;
1541 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001542 resolved = *resolveptr;
1543 }
1544 else if (propertyMap.first == "Path")
1545 {
1546 filePath = std::get_if<std::string>(&propertyMap.second);
1547 }
1548 }
1549 if (id == nullptr || message == nullptr || severity == nullptr ||
1550 timestamp == nullptr || updateTimestamp == nullptr)
1551 {
1552 messages::internalError(asyncResp->res);
1553 return;
1554 }
1555 asyncResp->res.jsonValue["@odata.type"] =
1556 "#LogEntry.v1_8_0.LogEntry";
1557 asyncResp->res.jsonValue["@odata.id"] =
1558 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1559 std::to_string(*id);
1560 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1561 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1562 asyncResp->res.jsonValue["Message"] = *message;
1563 asyncResp->res.jsonValue["Resolved"] = resolved;
1564 asyncResp->res.jsonValue["EntryType"] = "Event";
1565 asyncResp->res.jsonValue["Severity"] =
1566 translateSeverityDbusToRedfish(*severity);
1567 asyncResp->res.jsonValue["Created"] =
1568 crow::utility::getDateTimeUintMs(*timestamp);
1569 asyncResp->res.jsonValue["Modified"] =
1570 crow::utility::getDateTimeUintMs(*updateTimestamp);
1571 if (filePath != nullptr)
1572 {
1573 asyncResp->res.jsonValue["AdditionalDataURI"] =
1574 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1575 std::to_string(*id) + "/attachment";
1576 }
1577 },
1578 "xyz.openbmc_project.Logging",
1579 "/xyz/openbmc_project/logging/entry/" + entryID,
1580 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001581 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001582
1583 BMCWEB_ROUTE(
1584 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001585 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001586 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001587 [&app](const crow::Request& req,
1588 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1589 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001590 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001591 {
1592 return;
1593 }
1594 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001595
Ed Tanous002d39b2022-05-31 08:59:27 -07001596 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1597 resolved))
1598 {
1599 return;
1600 }
1601 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001602
Ed Tanous002d39b2022-05-31 08:59:27 -07001603 crow::connections::systemBus->async_method_call(
1604 [asyncResp, entryId](const boost::system::error_code ec) {
1605 if (ec)
1606 {
1607 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1608 messages::internalError(asyncResp->res);
1609 return;
1610 }
1611 },
1612 "xyz.openbmc_project.Logging",
1613 "/xyz/openbmc_project/logging/entry/" + entryId,
1614 "org.freedesktop.DBus.Properties", "Set",
1615 "xyz.openbmc_project.Logging.Entry", "Resolved",
1616 dbus::utility::DbusVariantType(*resolved));
1617 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001618
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001619 BMCWEB_ROUTE(
1620 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001621 .privileges(redfish::privileges::deleteLogEntry)
1622
Ed Tanous002d39b2022-05-31 08:59:27 -07001623 .methods(boost::beast::http::verb::delete_)(
1624 [&app](const crow::Request& req,
1625 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1626 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001627 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001628 {
1629 return;
1630 }
1631 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1632
1633 std::string entryID = param;
1634
1635 dbus::utility::escapePathForDbus(entryID);
1636
1637 // Process response from Logging service.
1638 auto respHandler =
1639 [asyncResp, entryID](const boost::system::error_code ec) {
1640 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1641 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001642 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001643 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001644 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001645 messages::resourceNotFound(asyncResp->res, "LogEntry",
1646 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001647 return;
1648 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001649 // TODO Handle for specific error code
1650 BMCWEB_LOG_ERROR
1651 << "EventLogEntry (DBus) doDelete respHandler got error "
1652 << ec;
1653 asyncResp->res.result(
1654 boost::beast::http::status::internal_server_error);
1655 return;
1656 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001657
Ed Tanous002d39b2022-05-31 08:59:27 -07001658 asyncResp->res.result(boost::beast::http::status::ok);
1659 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660
Ed Tanous002d39b2022-05-31 08:59:27 -07001661 // Make call to Logging service to request Delete Log
1662 crow::connections::systemBus->async_method_call(
1663 respHandler, "xyz.openbmc_project.Logging",
1664 "/xyz/openbmc_project/logging/entry/" + entryID,
1665 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001666 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001667}
1668
1669inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001670{
George Liu0fda0f12021-11-16 10:06:17 +08001671 BMCWEB_ROUTE(
1672 app,
1673 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001674 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001675 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001676 [&app](const crow::Request& req,
1677 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1678 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001679 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001680 {
1681 return;
1682 }
1683 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1684 {
1685 asyncResp->res.result(boost::beast::http::status::bad_request);
1686 return;
1687 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001688
Ed Tanous002d39b2022-05-31 08:59:27 -07001689 std::string entryID = param;
1690 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001691
Ed Tanous002d39b2022-05-31 08:59:27 -07001692 crow::connections::systemBus->async_method_call(
1693 [asyncResp, entryID](const boost::system::error_code ec,
1694 const sdbusplus::message::unix_fd& unixfd) {
1695 if (ec.value() == EBADR)
1696 {
1697 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1698 entryID);
1699 return;
1700 }
1701 if (ec)
1702 {
1703 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1704 messages::internalError(asyncResp->res);
1705 return;
1706 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001707
Ed Tanous002d39b2022-05-31 08:59:27 -07001708 int fd = -1;
1709 fd = dup(unixfd);
1710 if (fd == -1)
1711 {
1712 messages::internalError(asyncResp->res);
1713 return;
1714 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001715
Ed Tanous002d39b2022-05-31 08:59:27 -07001716 long long int size = lseek(fd, 0, SEEK_END);
1717 if (size == -1)
1718 {
1719 messages::internalError(asyncResp->res);
1720 return;
1721 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001722
Ed Tanous002d39b2022-05-31 08:59:27 -07001723 // Arbitrary max size of 64kb
1724 constexpr int maxFileSize = 65536;
1725 if (size > maxFileSize)
1726 {
1727 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1728 << maxFileSize;
1729 messages::internalError(asyncResp->res);
1730 return;
1731 }
1732 std::vector<char> data(static_cast<size_t>(size));
1733 long long int rc = lseek(fd, 0, SEEK_SET);
1734 if (rc == -1)
1735 {
1736 messages::internalError(asyncResp->res);
1737 return;
1738 }
1739 rc = read(fd, data.data(), data.size());
1740 if ((rc == -1) || (rc != size))
1741 {
1742 messages::internalError(asyncResp->res);
1743 return;
1744 }
1745 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001746
Ed Tanous002d39b2022-05-31 08:59:27 -07001747 std::string_view strData(data.data(), data.size());
1748 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001749
Ed Tanousd9f6c622022-03-17 09:12:17 -07001750 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07001751 "application/octet-stream");
Ed Tanousd9f6c622022-03-17 09:12:17 -07001752 asyncResp->res.addHeader(
1753 boost::beast::http::field::content_transfer_encoding, "Base64");
Ed Tanous002d39b2022-05-31 08:59:27 -07001754 asyncResp->res.body() = std::move(output);
1755 },
1756 "xyz.openbmc_project.Logging",
1757 "/xyz/openbmc_project/logging/entry/" + entryID,
1758 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1759 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001760}
1761
Spencer Kub7028eb2021-10-26 15:27:35 +08001762constexpr const char* hostLoggerFolderPath = "/var/log/console";
1763
1764inline bool
1765 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1766 std::vector<std::filesystem::path>& hostLoggerFiles)
1767{
1768 std::error_code ec;
1769 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1770 if (ec)
1771 {
1772 BMCWEB_LOG_ERROR << ec.message();
1773 return false;
1774 }
1775 for (const std::filesystem::directory_entry& it : logPath)
1776 {
1777 std::string filename = it.path().filename();
1778 // Prefix of each log files is "log". Find the file and save the
1779 // path
Ed Tanous11ba3972022-07-11 09:50:41 -07001780 if (filename.starts_with("log"))
Spencer Kub7028eb2021-10-26 15:27:35 +08001781 {
1782 hostLoggerFiles.emplace_back(it.path());
1783 }
1784 }
1785 // As the log files rotate, they are appended with a ".#" that is higher for
1786 // the older logs. Since we start from oldest logs, sort the name in
1787 // descending order.
1788 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1789 AlphanumLess<std::string>());
1790
1791 return true;
1792}
1793
Ed Tanous02cad962022-06-30 16:50:15 -07001794inline bool getHostLoggerEntries(
1795 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1796 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001797{
1798 GzFileReader logFile;
1799
1800 // Go though all log files and expose host logs.
1801 for (const std::filesystem::path& it : hostLoggerFiles)
1802 {
1803 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1804 {
1805 BMCWEB_LOG_ERROR << "fail to expose host logs";
1806 return false;
1807 }
1808 }
1809 // Get lastMessage from constructor by getter
1810 std::string lastMessage = logFile.getLastMessage();
1811 if (!lastMessage.empty())
1812 {
1813 logCount++;
1814 if (logCount > skip && logCount <= (skip + top))
1815 {
1816 logEntries.push_back(lastMessage);
1817 }
1818 }
1819 return true;
1820}
1821
1822inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1823 const std::string& msg,
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001824 nlohmann::json::object_t& logEntryJson)
Spencer Kub7028eb2021-10-26 15:27:35 +08001825{
1826 // Fill in the log entry with the gathered data.
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001827 logEntryJson["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
1828 logEntryJson["@odata.id"] =
1829 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1830 logEntryID;
1831 logEntryJson["Name"] = "Host Logger Entry";
1832 logEntryJson["Id"] = logEntryID;
1833 logEntryJson["Message"] = msg;
1834 logEntryJson["EntryType"] = "Oem";
1835 logEntryJson["Severity"] = "OK";
1836 logEntryJson["OemRecordFormat"] = "Host Logger Entry";
Spencer Kub7028eb2021-10-26 15:27:35 +08001837}
1838
1839inline void requestRoutesSystemHostLogger(App& app)
1840{
1841 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1842 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001843 .methods(boost::beast::http::verb::get)(
1844 [&app](const crow::Request& req,
1845 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001846 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001847 {
1848 return;
1849 }
1850 asyncResp->res.jsonValue["@odata.id"] =
1851 "/redfish/v1/Systems/system/LogServices/HostLogger";
1852 asyncResp->res.jsonValue["@odata.type"] =
1853 "#LogService.v1_1_0.LogService";
1854 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1855 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1856 asyncResp->res.jsonValue["Id"] = "HostLogger";
1857 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1858 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1859 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001860}
1861
1862inline void requestRoutesSystemHostLoggerCollection(App& app)
1863{
1864 BMCWEB_ROUTE(app,
1865 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1866 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001867 .methods(boost::beast::http::verb::get)(
1868 [&app](const crow::Request& req,
1869 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1870 query_param::QueryCapabilities capabilities = {
1871 .canDelegateTop = true,
1872 .canDelegateSkip = true,
1873 };
1874 query_param::Query delegatedQuery;
1875 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001876 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001877 {
1878 return;
1879 }
1880 asyncResp->res.jsonValue["@odata.id"] =
1881 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1882 asyncResp->res.jsonValue["@odata.type"] =
1883 "#LogEntryCollection.LogEntryCollection";
1884 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1885 asyncResp->res.jsonValue["Description"] =
1886 "Collection of HostLogger Entries";
1887 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1888 logEntryArray = nlohmann::json::array();
1889 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001890
Ed Tanous002d39b2022-05-31 08:59:27 -07001891 std::vector<std::filesystem::path> hostLoggerFiles;
1892 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1893 {
1894 BMCWEB_LOG_ERROR << "fail to get host log file path";
1895 return;
1896 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001897 // If we weren't provided top and skip limits, use the defaults.
1898 size_t skip = delegatedQuery.skip.value_or(0);
1899 size_t top =
1900 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
Ed Tanous002d39b2022-05-31 08:59:27 -07001901 size_t logCount = 0;
1902 // This vector only store the entries we want to expose that
1903 // control by skip and top.
1904 std::vector<std::string> logEntries;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001905 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1906 logCount))
Ed Tanous002d39b2022-05-31 08:59:27 -07001907 {
1908 messages::internalError(asyncResp->res);
1909 return;
1910 }
1911 // If vector is empty, that means skip value larger than total
1912 // log count
1913 if (logEntries.empty())
1914 {
1915 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1916 return;
1917 }
1918 if (!logEntries.empty())
1919 {
1920 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001921 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001922 nlohmann::json::object_t hostLogEntry;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001923 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
1924 hostLogEntry);
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001925 logEntryArray.push_back(std::move(hostLogEntry));
George Liu0fda0f12021-11-16 10:06:17 +08001926 }
1927
Ed Tanous002d39b2022-05-31 08:59:27 -07001928 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001929 if (skip + top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001930 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001931 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1932 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001933 std::to_string(skip + top);
George Liu0fda0f12021-11-16 10:06:17 +08001934 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001935 }
George Liu0fda0f12021-11-16 10:06:17 +08001936 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001937}
1938
1939inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1940{
1941 BMCWEB_ROUTE(
1942 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1943 .privileges(redfish::privileges::getLogEntry)
1944 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001945 [&app](const crow::Request& req,
1946 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1947 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001948 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001949 {
1950 return;
1951 }
1952 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001953
Ed Tanous002d39b2022-05-31 08:59:27 -07001954 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001955
Ed Tanous002d39b2022-05-31 08:59:27 -07001956 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1957 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001958
Ed Tanous002d39b2022-05-31 08:59:27 -07001959 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1960 if (ec == std::errc::invalid_argument)
1961 {
1962 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1963 return;
1964 }
1965 if (ec == std::errc::result_out_of_range)
1966 {
1967 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1968 return;
1969 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001970
Ed Tanous002d39b2022-05-31 08:59:27 -07001971 std::vector<std::filesystem::path> hostLoggerFiles;
1972 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1973 {
1974 BMCWEB_LOG_ERROR << "fail to get host log file path";
1975 return;
1976 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001977
Ed Tanous002d39b2022-05-31 08:59:27 -07001978 size_t logCount = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001979 size_t top = 1;
Ed Tanous002d39b2022-05-31 08:59:27 -07001980 std::vector<std::string> logEntries;
1981 // We can get specific entry by skip and top. For example, if we
1982 // want to get nth entry, we can set skip = n-1 and top = 1 to
1983 // get that entry
1984 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1985 logCount))
1986 {
1987 messages::internalError(asyncResp->res);
1988 return;
1989 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001990
Ed Tanous002d39b2022-05-31 08:59:27 -07001991 if (!logEntries.empty())
1992 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001993 nlohmann::json::object_t hostLogEntry;
1994 fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
1995 asyncResp->res.jsonValue.update(hostLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07001996 return;
1997 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001998
Ed Tanous002d39b2022-05-31 08:59:27 -07001999 // Requested ID was not found
2000 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2001 });
Spencer Kub7028eb2021-10-26 15:27:35 +08002002}
2003
Claire Weinanfdd26902022-03-01 14:18:25 -08002004constexpr char const* dumpManagerIface =
2005 "xyz.openbmc_project.Collection.DeleteAll";
2006inline void handleLogServicesCollectionGet(
2007 crow::App& app, const crow::Request& req,
2008 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2009{
2010 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2011 {
2012 return;
2013 }
2014 // Collections don't include the static data added by SubRoute
2015 // because it has a duplicate entry for members
2016 asyncResp->res.jsonValue["@odata.type"] =
2017 "#LogServiceCollection.LogServiceCollection";
2018 asyncResp->res.jsonValue["@odata.id"] =
2019 "/redfish/v1/Managers/bmc/LogServices";
2020 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2021 asyncResp->res.jsonValue["Description"] =
2022 "Collection of LogServices for this Manager";
2023 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2024 logServiceArray = nlohmann::json::array();
2025
2026#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
2027 logServiceArray.push_back(
2028 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
2029#endif
2030
2031 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2032
2033#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2034 auto respHandler =
2035 [asyncResp](
2036 const boost::system::error_code ec,
2037 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2038 if (ec)
2039 {
2040 BMCWEB_LOG_ERROR
2041 << "handleLogServicesCollectionGet respHandler got error "
2042 << ec;
2043 // Assume that getting an error simply means there are no dump
2044 // LogServices. Return without adding any error response.
2045 return;
2046 }
2047
2048 nlohmann::json& logServiceArrayLocal =
2049 asyncResp->res.jsonValue["Members"];
2050
2051 for (const std::string& path : subTreePaths)
2052 {
2053 if (path == "/xyz/openbmc_project/dump/bmc")
2054 {
2055 logServiceArrayLocal.push_back(
2056 {{"@odata.id",
2057 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
2058 }
2059 else if (path == "/xyz/openbmc_project/dump/faultlog")
2060 {
2061 logServiceArrayLocal.push_back(
2062 {{"@odata.id",
2063 "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
2064 }
2065 }
2066
2067 asyncResp->res.jsonValue["Members@odata.count"] =
2068 logServiceArrayLocal.size();
2069 };
2070
2071 crow::connections::systemBus->async_method_call(
2072 respHandler, "xyz.openbmc_project.ObjectMapper",
2073 "/xyz/openbmc_project/object_mapper",
2074 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2075 "/xyz/openbmc_project/dump", 0,
2076 std::array<const char*, 1>{dumpManagerIface});
2077#endif
2078}
2079
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002080inline void requestRoutesBMCLogServiceCollection(App& app)
2081{
2082 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002083 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002084 .methods(boost::beast::http::verb::get)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002085 std::bind_front(handleLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002086}
Ed Tanous1da66f72018-07-27 16:13:37 -07002087
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002088inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002089{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002090 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002091 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002092 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002093 [&app](const crow::Request& req,
2094 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002095 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002096 {
2097 return;
2098 }
2099 asyncResp->res.jsonValue["@odata.type"] =
2100 "#LogService.v1_1_0.LogService";
2101 asyncResp->res.jsonValue["@odata.id"] =
2102 "/redfish/v1/Managers/bmc/LogServices/Journal";
2103 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2104 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2105 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2106 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302107
Ed Tanous002d39b2022-05-31 08:59:27 -07002108 std::pair<std::string, std::string> redfishDateTimeOffset =
2109 crow::utility::getDateTimeOffsetNow();
2110 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2111 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2112 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302113
Ed Tanous002d39b2022-05-31 08:59:27 -07002114 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2115 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2116 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002117}
Jason M. Billse1f26342018-07-18 12:12:00 -07002118
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002119static int
2120 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2121 sd_journal* journal,
2122 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002123{
2124 // Get the Log Entry contents
2125 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002126
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002127 std::string message;
2128 std::string_view syslogID;
2129 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2130 if (ret < 0)
2131 {
2132 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2133 << strerror(-ret);
2134 }
2135 if (!syslogID.empty())
2136 {
2137 message += std::string(syslogID) + ": ";
2138 }
2139
Ed Tanous39e77502019-03-04 17:35:53 -08002140 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002141 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002142 if (ret < 0)
2143 {
2144 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2145 return 1;
2146 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002147 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002148
2149 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002150 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002151 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002152 if (ret < 0)
2153 {
2154 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002155 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002156
2157 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002158 std::string entryTimeStr;
2159 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002160 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002161 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002162 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002163
2164 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07002165 bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
2166 bmcJournalLogEntryJson["@odata.id"] =
2167 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2168 bmcJournalLogEntryID;
2169 bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2170 bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2171 bmcJournalLogEntryJson["Message"] = std::move(message);
2172 bmcJournalLogEntryJson["EntryType"] = "Oem";
2173 bmcJournalLogEntryJson["Severity"] = severity <= 2 ? "Critical"
2174 : severity <= 4 ? "Warning"
2175 : "OK";
2176 bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2177 bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
Jason M. Billse1f26342018-07-18 12:12:00 -07002178 return 0;
2179}
2180
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002181inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002182{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002183 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002184 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002185 .methods(boost::beast::http::verb::get)(
2186 [&app](const crow::Request& req,
2187 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2188 query_param::QueryCapabilities capabilities = {
2189 .canDelegateTop = true,
2190 .canDelegateSkip = true,
2191 };
2192 query_param::Query delegatedQuery;
2193 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002194 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002195 {
2196 return;
2197 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07002198
2199 size_t skip = delegatedQuery.skip.value_or(0);
2200 size_t top =
2201 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
2202
Ed Tanous002d39b2022-05-31 08:59:27 -07002203 // Collections don't include the static data added by SubRoute
2204 // because it has a duplicate entry for members
2205 asyncResp->res.jsonValue["@odata.type"] =
2206 "#LogEntryCollection.LogEntryCollection";
2207 asyncResp->res.jsonValue["@odata.id"] =
2208 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2209 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2210 asyncResp->res.jsonValue["Description"] =
2211 "Collection of BMC Journal Entries";
2212 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2213 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002214
Ed Tanous002d39b2022-05-31 08:59:27 -07002215 // Go through the journal and use the timestamp to create a
2216 // unique ID for each entry
2217 sd_journal* journalTmp = nullptr;
2218 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2219 if (ret < 0)
2220 {
2221 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2222 messages::internalError(asyncResp->res);
2223 return;
2224 }
2225 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2226 journalTmp, sd_journal_close);
2227 journalTmp = nullptr;
2228 uint64_t entryCount = 0;
2229 // Reset the unique ID on the first entry
2230 bool firstEntry = true;
2231 SD_JOURNAL_FOREACH(journal.get())
2232 {
2233 entryCount++;
2234 // Handle paging using skip (number of entries to skip from
2235 // the start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07002236 if (entryCount <= skip || entryCount > skip + top)
George Liu0fda0f12021-11-16 10:06:17 +08002237 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002238 continue;
2239 }
2240
2241 std::string idStr;
2242 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2243 {
2244 continue;
2245 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002246 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002247
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002248 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002249 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2250 bmcJournalLogEntry) != 0)
2251 {
George Liu0fda0f12021-11-16 10:06:17 +08002252 messages::internalError(asyncResp->res);
2253 return;
2254 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002255 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002256 }
2257 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07002258 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07002259 {
2260 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2261 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07002262 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07002263 }
George Liu0fda0f12021-11-16 10:06:17 +08002264 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002265}
Jason M. Billse1f26342018-07-18 12:12:00 -07002266
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002267inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002268{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002269 BMCWEB_ROUTE(app,
2270 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002271 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002272 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002273 [&app](const crow::Request& req,
2274 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2275 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002276 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002277 {
2278 return;
2279 }
2280 // Convert the unique ID back to a timestamp to find the entry
2281 uint64_t ts = 0;
2282 uint64_t index = 0;
2283 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2284 {
2285 return;
2286 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002287
Ed Tanous002d39b2022-05-31 08:59:27 -07002288 sd_journal* journalTmp = nullptr;
2289 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2290 if (ret < 0)
2291 {
2292 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2293 messages::internalError(asyncResp->res);
2294 return;
2295 }
2296 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2297 journalTmp, sd_journal_close);
2298 journalTmp = nullptr;
2299 // Go to the timestamp in the log and move to the entry at the
2300 // index tracking the unique ID
2301 std::string idStr;
2302 bool firstEntry = true;
2303 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2304 if (ret < 0)
2305 {
2306 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2307 << strerror(-ret);
2308 messages::internalError(asyncResp->res);
2309 return;
2310 }
2311 for (uint64_t i = 0; i <= index; i++)
2312 {
2313 sd_journal_next(journal.get());
2314 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2315 {
2316 messages::internalError(asyncResp->res);
2317 return;
2318 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002319 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002320 }
2321 // Confirm that the entry ID matches what was requested
2322 if (idStr != entryID)
2323 {
2324 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2325 return;
2326 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002327
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002328 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002329 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002330 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002331 {
2332 messages::internalError(asyncResp->res);
2333 return;
2334 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07002335 asyncResp->res.jsonValue.update(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002336 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002337}
2338
Claire Weinanfdd26902022-03-01 14:18:25 -08002339inline void
2340 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2341 const std::string& dumpType)
2342{
2343 std::string dumpPath;
2344 std::string overWritePolicy;
2345 bool collectDiagnosticDataSupported = false;
2346
2347 if (dumpType == "BMC")
2348 {
2349 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2350 overWritePolicy = "WrapsWhenFull";
2351 collectDiagnosticDataSupported = true;
2352 }
2353 else if (dumpType == "FaultLog")
2354 {
2355 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2356 overWritePolicy = "Unknown";
2357 collectDiagnosticDataSupported = false;
2358 }
2359 else if (dumpType == "System")
2360 {
2361 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2362 overWritePolicy = "WrapsWhenFull";
2363 collectDiagnosticDataSupported = true;
2364 }
2365 else
2366 {
2367 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2368 << dumpType;
2369 messages::internalError(asyncResp->res);
2370 return;
2371 }
2372
2373 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2374 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2375 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2376 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2377 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2378 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2379
2380 std::pair<std::string, std::string> redfishDateTimeOffset =
2381 crow::utility::getDateTimeOffsetNow();
2382 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2383 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2384 redfishDateTimeOffset.second;
2385
2386 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2387 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2388 dumpPath + "/Actions/LogService.ClearLog";
2389
2390 if (collectDiagnosticDataSupported)
2391 {
2392 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2393 ["target"] =
2394 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2395 }
2396}
2397
2398inline void handleLogServicesDumpServiceGet(
2399 crow::App& app, const std::string& dumpType, const crow::Request& req,
2400 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2401{
2402 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2403 {
2404 return;
2405 }
2406 getDumpServiceInfo(asyncResp, dumpType);
2407}
2408
2409inline void handleLogServicesDumpEntriesCollectionGet(
2410 crow::App& app, const std::string& dumpType, const crow::Request& req,
2411 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2412{
2413 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2414 {
2415 return;
2416 }
2417 getDumpEntryCollection(asyncResp, dumpType);
2418}
2419
2420inline void handleLogServicesDumpEntryGet(
2421 crow::App& app, const std::string& dumpType, const crow::Request& req,
2422 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2423 const std::string& dumpId)
2424{
2425 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2426 {
2427 return;
2428 }
2429 getDumpEntryById(asyncResp, dumpId, dumpType);
2430}
2431
2432inline void handleLogServicesDumpEntryDelete(
2433 crow::App& app, const std::string& dumpType, const crow::Request& req,
2434 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2435 const std::string& dumpId)
2436{
2437 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2438 {
2439 return;
2440 }
2441 deleteDumpEntry(asyncResp, dumpId, dumpType);
2442}
2443
2444inline void handleLogServicesDumpCollectDiagnosticDataPost(
2445 crow::App& app, const std::string& dumpType, const crow::Request& req,
2446 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2447{
2448 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2449 {
2450 return;
2451 }
2452 createDump(asyncResp, req, dumpType);
2453}
2454
2455inline void handleLogServicesDumpClearLogPost(
2456 crow::App& app, const std::string& dumpType, const crow::Request& req,
2457 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2458{
2459 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2460 {
2461 return;
2462 }
2463 clearDump(asyncResp, dumpType);
2464}
2465
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002466inline void requestRoutesBMCDumpService(App& app)
2467{
2468 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002469 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002470 .methods(boost::beast::http::verb::get)(std::bind_front(
2471 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002472}
2473
2474inline void requestRoutesBMCDumpEntryCollection(App& app)
2475{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002476 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002477 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002478 .methods(boost::beast::http::verb::get)(std::bind_front(
2479 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002480}
2481
2482inline void requestRoutesBMCDumpEntry(App& app)
2483{
2484 BMCWEB_ROUTE(app,
2485 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002486 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002487 .methods(boost::beast::http::verb::get)(std::bind_front(
2488 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2489
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002490 BMCWEB_ROUTE(app,
2491 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002492 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002493 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2494 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002495}
2496
2497inline void requestRoutesBMCDumpCreate(App& app)
2498{
George Liu0fda0f12021-11-16 10:06:17 +08002499 BMCWEB_ROUTE(
2500 app,
2501 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002502 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002503 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002504 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2505 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002506}
2507
2508inline void requestRoutesBMCDumpClear(App& app)
2509{
George Liu0fda0f12021-11-16 10:06:17 +08002510 BMCWEB_ROUTE(
2511 app,
2512 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002513 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002514 .methods(boost::beast::http::verb::post)(std::bind_front(
2515 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2516}
2517
2518inline void requestRoutesFaultLogDumpService(App& app)
2519{
2520 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2521 .privileges(redfish::privileges::getLogService)
2522 .methods(boost::beast::http::verb::get)(std::bind_front(
2523 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2524}
2525
2526inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2527{
2528 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2529 .privileges(redfish::privileges::getLogEntryCollection)
2530 .methods(boost::beast::http::verb::get)(
2531 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2532 std::ref(app), "FaultLog"));
2533}
2534
2535inline void requestRoutesFaultLogDumpEntry(App& app)
2536{
2537 BMCWEB_ROUTE(app,
2538 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2539 .privileges(redfish::privileges::getLogEntry)
2540 .methods(boost::beast::http::verb::get)(std::bind_front(
2541 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2542
2543 BMCWEB_ROUTE(app,
2544 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2545 .privileges(redfish::privileges::deleteLogEntry)
2546 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2547 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2548}
2549
2550inline void requestRoutesFaultLogDumpClear(App& app)
2551{
2552 BMCWEB_ROUTE(
2553 app,
2554 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2555 .privileges(redfish::privileges::postLogService)
2556 .methods(boost::beast::http::verb::post)(std::bind_front(
2557 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002558}
2559
2560inline void requestRoutesSystemDumpService(App& app)
2561{
2562 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002563 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002564 .methods(boost::beast::http::verb::get)(
2565 [&app](const crow::Request& req,
2566 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002567 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002568 {
2569 return;
2570 }
2571 asyncResp->res.jsonValue["@odata.id"] =
2572 "/redfish/v1/Systems/system/LogServices/Dump";
2573 asyncResp->res.jsonValue["@odata.type"] =
2574 "#LogService.v1_2_0.LogService";
2575 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2576 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2577 asyncResp->res.jsonValue["Id"] = "Dump";
2578 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302579
Ed Tanous002d39b2022-05-31 08:59:27 -07002580 std::pair<std::string, std::string> redfishDateTimeOffset =
2581 crow::utility::getDateTimeOffsetNow();
2582 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2583 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2584 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302585
Ed Tanous002d39b2022-05-31 08:59:27 -07002586 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2587 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2588 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2589 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002590
Ed Tanous002d39b2022-05-31 08:59:27 -07002591 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2592 ["target"] =
2593 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002594 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002595}
2596
2597inline void requestRoutesSystemDumpEntryCollection(App& app)
2598{
2599
2600 /**
2601 * Functions triggers appropriate requests on DBus
2602 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002603 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002604 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002605 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002606 [&app](const crow::Request& req,
2607 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002608 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002609 {
2610 return;
2611 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002612 getDumpEntryCollection(asyncResp, "System");
2613 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002614}
2615
2616inline void requestRoutesSystemDumpEntry(App& app)
2617{
2618 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002619 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002620 .privileges(redfish::privileges::getLogEntry)
2621
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002622 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002623 [&app](const crow::Request& req,
2624 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2625 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002626 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002627 {
2628 return;
2629 }
2630 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002631 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002632
2633 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002634 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002635 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002636 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002637 [&app](const crow::Request& req,
2638 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2639 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002640 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002641 {
2642 return;
2643 }
2644 deleteDumpEntry(asyncResp, param, "system");
2645 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002646}
2647
2648inline void requestRoutesSystemDumpCreate(App& app)
2649{
George Liu0fda0f12021-11-16 10:06:17 +08002650 BMCWEB_ROUTE(
2651 app,
2652 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002653 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002654 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002655 [&app](const crow::Request& req,
2656 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002657 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002658 {
2659 return;
2660 }
2661 createDump(asyncResp, req, "System");
2662 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002663}
2664
2665inline void requestRoutesSystemDumpClear(App& app)
2666{
George Liu0fda0f12021-11-16 10:06:17 +08002667 BMCWEB_ROUTE(
2668 app,
2669 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002670 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002671 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002672 [&app](const crow::Request& req,
2673 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002674
Ed Tanous45ca1b82022-03-25 13:07:27 -07002675 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002676 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002677 {
2678 return;
2679 }
2680 clearDump(asyncResp, "System");
2681 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002682}
2683
2684inline void requestRoutesCrashdumpService(App& app)
2685{
2686 // Note: Deviated from redfish privilege registry for GET & HEAD
2687 // method for security reasons.
2688 /**
2689 * Functions triggers appropriate requests on DBus
2690 */
2691 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002692 // This is incorrect, should be:
2693 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002694 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002695 .methods(boost::beast::http::verb::get)(
2696 [&app](const crow::Request& req,
2697 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002698 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002699 {
2700 return;
2701 }
2702 // Copy over the static data to include the entries added by
2703 // SubRoute
2704 asyncResp->res.jsonValue["@odata.id"] =
2705 "/redfish/v1/Systems/system/LogServices/Crashdump";
2706 asyncResp->res.jsonValue["@odata.type"] =
2707 "#LogService.v1_2_0.LogService";
2708 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2709 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2710 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2711 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2712 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302713
Ed Tanous002d39b2022-05-31 08:59:27 -07002714 std::pair<std::string, std::string> redfishDateTimeOffset =
2715 crow::utility::getDateTimeOffsetNow();
2716 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2717 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2718 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302719
Ed Tanous002d39b2022-05-31 08:59:27 -07002720 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2721 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2722 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2723 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2724 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2725 ["target"] =
2726 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002727 });
2728}
2729
2730void inline requestRoutesCrashdumpClear(App& app)
2731{
George Liu0fda0f12021-11-16 10:06:17 +08002732 BMCWEB_ROUTE(
2733 app,
2734 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002735 // This is incorrect, should be:
2736 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002737 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002738 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002739 [&app](const crow::Request& req,
2740 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002741 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002742 {
2743 return;
2744 }
2745 crow::connections::systemBus->async_method_call(
2746 [asyncResp](const boost::system::error_code ec,
2747 const std::string&) {
2748 if (ec)
2749 {
2750 messages::internalError(asyncResp->res);
2751 return;
2752 }
2753 messages::success(asyncResp->res);
2754 },
2755 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2756 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002757}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002758
zhanghch058d1b46d2021-04-01 11:18:24 +08002759static void
2760 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2761 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002762{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002763 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002764 [asyncResp, logID,
2765 &logEntryJson](const boost::system::error_code ec,
2766 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002767 if (ec)
2768 {
2769 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2770 if (ec.value() ==
2771 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002772 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002773 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002774 }
2775 else
2776 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002777 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002778 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002779 return;
2780 }
2781
2782 std::string timestamp{};
2783 std::string filename{};
2784 std::string logfile{};
2785 parseCrashdumpParameters(params, filename, timestamp, logfile);
2786
2787 if (filename.empty() || timestamp.empty())
2788 {
2789 messages::resourceMissingAtURI(asyncResp->res,
2790 crow::utility::urlFromPieces(logID));
2791 return;
2792 }
2793
2794 std::string crashdumpURI =
2795 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2796 logID + "/" + filename;
Jason M. Bills84afc482022-06-24 12:38:23 -07002797 nlohmann::json::object_t logEntry;
2798 logEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
2799 logEntry["@odata.id"] =
2800 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
2801 logEntry["Name"] = "CPU Crashdump";
2802 logEntry["Id"] = logID;
2803 logEntry["EntryType"] = "Oem";
2804 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2805 logEntry["DiagnosticDataType"] = "OEM";
2806 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2807 logEntry["Created"] = std::move(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07002808
2809 // If logEntryJson references an array of LogEntry resources
2810 // ('Members' list), then push this as a new entry, otherwise set it
2811 // directly
2812 if (logEntryJson.is_array())
2813 {
2814 logEntryJson.push_back(logEntry);
2815 asyncResp->res.jsonValue["Members@odata.count"] =
2816 logEntryJson.size();
2817 }
2818 else
2819 {
Jason M. Billsd405bb52022-06-24 10:52:05 -07002820 logEntryJson.update(logEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002821 }
2822 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002823 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002824 std::move(getStoredLogCallback), crashdumpObject,
2825 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002826 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002827}
2828
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002829inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002830{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002831 // Note: Deviated from redfish privilege registry for GET & HEAD
2832 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002833 /**
2834 * Functions triggers appropriate requests on DBus
2835 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002836 BMCWEB_ROUTE(app,
2837 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002838 // This is incorrect, should be.
2839 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002840 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002841 .methods(boost::beast::http::verb::get)(
2842 [&app](const crow::Request& req,
2843 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002844 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002845 {
2846 return;
2847 }
2848 crow::connections::systemBus->async_method_call(
2849 [asyncResp](const boost::system::error_code ec,
2850 const std::vector<std::string>& resp) {
2851 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002852 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002853 if (ec.value() !=
2854 boost::system::errc::no_such_file_or_directory)
2855 {
2856 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2857 << ec.message();
2858 messages::internalError(asyncResp->res);
2859 return;
2860 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002861 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002862 asyncResp->res.jsonValue["@odata.type"] =
2863 "#LogEntryCollection.LogEntryCollection";
2864 asyncResp->res.jsonValue["@odata.id"] =
2865 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2866 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2867 asyncResp->res.jsonValue["Description"] =
2868 "Collection of Crashdump Entries";
2869 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2870 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002871
Ed Tanous002d39b2022-05-31 08:59:27 -07002872 for (const std::string& path : resp)
2873 {
2874 const sdbusplus::message::object_path objPath(path);
2875 // Get the log ID
2876 std::string logID = objPath.filename();
2877 if (logID.empty())
2878 {
2879 continue;
2880 }
2881 // Add the log entry to the array
2882 logCrashdumpEntry(asyncResp, logID,
2883 asyncResp->res.jsonValue["Members"]);
2884 }
2885 },
2886 "xyz.openbmc_project.ObjectMapper",
2887 "/xyz/openbmc_project/object_mapper",
2888 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2889 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002890 });
2891}
Ed Tanous1da66f72018-07-27 16:13:37 -07002892
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002893inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002894{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002895 // Note: Deviated from redfish privilege registry for GET & HEAD
2896 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002897
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002898 BMCWEB_ROUTE(
2899 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002900 // this is incorrect, should be
2901 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002902 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002903 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002904 [&app](const crow::Request& req,
2905 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2906 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002907 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002908 {
2909 return;
2910 }
2911 const std::string& logID = param;
2912 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2913 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002914}
Ed Tanous1da66f72018-07-27 16:13:37 -07002915
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002916inline void requestRoutesCrashdumpFile(App& app)
2917{
2918 // Note: Deviated from redfish privilege registry for GET & HEAD
2919 // method for security reasons.
2920 BMCWEB_ROUTE(
2921 app,
2922 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002923 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002924 .methods(boost::beast::http::verb::get)(
Nan Zhoua4ce1142022-08-02 18:45:25 +00002925 [](const crow::Request& req,
2926 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2927 const std::string& logID, const std::string& fileName) {
Shounak Mitra2a9beee2022-07-20 18:41:30 +00002928 // Do not call getRedfishRoute here since the crashdump file is not a
2929 // Redfish resource.
Ed Tanous002d39b2022-05-31 08:59:27 -07002930 auto getStoredLogCallback =
2931 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2932 const boost::system::error_code ec,
2933 const std::vector<
2934 std::pair<std::string, dbus::utility::DbusVariantType>>&
2935 resp) {
2936 if (ec)
2937 {
2938 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2939 messages::internalError(asyncResp->res);
2940 return;
2941 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002942
Ed Tanous002d39b2022-05-31 08:59:27 -07002943 std::string dbusFilename{};
2944 std::string dbusTimestamp{};
2945 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002946
Ed Tanous002d39b2022-05-31 08:59:27 -07002947 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2948 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002949
Ed Tanous002d39b2022-05-31 08:59:27 -07002950 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2951 dbusFilepath.empty())
2952 {
2953 messages::resourceMissingAtURI(asyncResp->res, url);
2954 return;
2955 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002956
Ed Tanous002d39b2022-05-31 08:59:27 -07002957 // Verify the file name parameter is correct
2958 if (fileName != dbusFilename)
2959 {
2960 messages::resourceMissingAtURI(asyncResp->res, url);
2961 return;
2962 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002963
Ed Tanous002d39b2022-05-31 08:59:27 -07002964 if (!std::filesystem::exists(dbusFilepath))
2965 {
2966 messages::resourceMissingAtURI(asyncResp->res, url);
2967 return;
2968 }
2969 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2970 asyncResp->res.body() =
2971 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002972
Ed Tanous002d39b2022-05-31 08:59:27 -07002973 // Configure this to be a file download when accessed
2974 // from a browser
Ed Tanousd9f6c622022-03-17 09:12:17 -07002975 asyncResp->res.addHeader(
2976 boost::beast::http::field::content_disposition, "attachment");
Ed Tanous002d39b2022-05-31 08:59:27 -07002977 };
2978 crow::connections::systemBus->async_method_call(
2979 std::move(getStoredLogCallback), crashdumpObject,
2980 crashdumpPath + std::string("/") + logID,
2981 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2982 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002983}
2984
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002985enum class OEMDiagnosticType
2986{
2987 onDemand,
2988 telemetry,
2989 invalid,
2990};
2991
Ed Tanousf7725d72022-03-07 12:46:00 -08002992inline OEMDiagnosticType
2993 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002994{
2995 if (oemDiagStr == "OnDemand")
2996 {
2997 return OEMDiagnosticType::onDemand;
2998 }
2999 if (oemDiagStr == "Telemetry")
3000 {
3001 return OEMDiagnosticType::telemetry;
3002 }
3003
3004 return OEMDiagnosticType::invalid;
3005}
3006
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003007inline void requestRoutesCrashdumpCollect(App& app)
3008{
3009 // Note: Deviated from redfish privilege registry for GET & HEAD
3010 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08003011 BMCWEB_ROUTE(
3012 app,
3013 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07003014 // The below is incorrect; Should be ConfigureManager
3015 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003016 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07003017 .methods(boost::beast::http::verb::post)(
3018 [&app](const crow::Request& req,
3019 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003020 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003021 {
3022 return;
3023 }
3024 std::string diagnosticDataType;
3025 std::string oemDiagnosticDataType;
3026 if (!redfish::json_util::readJsonAction(
3027 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3028 "OEMDiagnosticDataType", oemDiagnosticDataType))
3029 {
3030 return;
3031 }
3032
3033 if (diagnosticDataType != "OEM")
3034 {
3035 BMCWEB_LOG_ERROR
3036 << "Only OEM DiagnosticDataType supported for Crashdump";
3037 messages::actionParameterValueFormatError(
3038 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3039 "CollectDiagnosticData");
3040 return;
3041 }
3042
3043 OEMDiagnosticType oemDiagType =
3044 getOEMDiagnosticType(oemDiagnosticDataType);
3045
3046 std::string iface;
3047 std::string method;
3048 std::string taskMatchStr;
3049 if (oemDiagType == OEMDiagnosticType::onDemand)
3050 {
3051 iface = crashdumpOnDemandInterface;
3052 method = "GenerateOnDemandLog";
3053 taskMatchStr = "type='signal',"
3054 "interface='org.freedesktop.DBus.Properties',"
3055 "member='PropertiesChanged',"
3056 "arg0namespace='com.intel.crashdump'";
3057 }
3058 else if (oemDiagType == OEMDiagnosticType::telemetry)
3059 {
3060 iface = crashdumpTelemetryInterface;
3061 method = "GenerateTelemetryLog";
3062 taskMatchStr = "type='signal',"
3063 "interface='org.freedesktop.DBus.Properties',"
3064 "member='PropertiesChanged',"
3065 "arg0namespace='com.intel.crashdump'";
3066 }
3067 else
3068 {
3069 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3070 << oemDiagnosticDataType;
3071 messages::actionParameterValueFormatError(
3072 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3073 "CollectDiagnosticData");
3074 return;
3075 }
3076
3077 auto collectCrashdumpCallback =
3078 [asyncResp, payload(task::Payload(req)),
3079 taskMatchStr](const boost::system::error_code ec,
3080 const std::string&) mutable {
3081 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003082 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003083 if (ec.value() == boost::system::errc::operation_not_supported)
3084 {
3085 messages::resourceInStandby(asyncResp->res);
3086 }
3087 else if (ec.value() ==
3088 boost::system::errc::device_or_resource_busy)
3089 {
3090 messages::serviceTemporarilyUnavailable(asyncResp->res,
3091 "60");
3092 }
3093 else
3094 {
3095 messages::internalError(asyncResp->res);
3096 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003097 return;
3098 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003099 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -05003100 [](boost::system::error_code err, sdbusplus::message_t&,
Ed Tanous002d39b2022-05-31 08:59:27 -07003101 const std::shared_ptr<task::TaskData>& taskData) {
3102 if (!err)
3103 {
3104 taskData->messages.emplace_back(messages::taskCompletedOK(
3105 std::to_string(taskData->index)));
3106 taskData->state = "Completed";
3107 }
3108 return task::completed;
3109 },
3110 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003111
Ed Tanous002d39b2022-05-31 08:59:27 -07003112 task->startTimer(std::chrono::minutes(5));
3113 task->populateResp(asyncResp->res);
3114 task->payload.emplace(std::move(payload));
3115 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003116
Ed Tanous002d39b2022-05-31 08:59:27 -07003117 crow::connections::systemBus->async_method_call(
3118 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3119 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003120 });
3121}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003122
Andrew Geisslercb92c032018-08-17 07:56:14 -07003123/**
3124 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3125 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003126inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003127{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003128 /**
3129 * Function handles POST method request.
3130 * The Clear Log actions does not require any parameter.The action deletes
3131 * all entries found in the Entries collection for this Log Service.
3132 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003133
George Liu0fda0f12021-11-16 10:06:17 +08003134 BMCWEB_ROUTE(
3135 app,
3136 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003137 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003138 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003139 [&app](const crow::Request& req,
3140 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003141 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003142 {
3143 return;
3144 }
3145 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003146
Ed Tanous002d39b2022-05-31 08:59:27 -07003147 // Process response from Logging service.
3148 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3149 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3150 if (ec)
3151 {
3152 // TODO Handle for specific error code
3153 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3154 asyncResp->res.result(
3155 boost::beast::http::status::internal_server_error);
3156 return;
3157 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003158
Ed Tanous002d39b2022-05-31 08:59:27 -07003159 asyncResp->res.result(boost::beast::http::status::no_content);
3160 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003161
Ed Tanous002d39b2022-05-31 08:59:27 -07003162 // Make call to Logging service to request Clear Log
3163 crow::connections::systemBus->async_method_call(
3164 respHandler, "xyz.openbmc_project.Logging",
3165 "/xyz/openbmc_project/logging",
3166 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3167 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003168}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003169
3170/****************************************************
3171 * Redfish PostCode interfaces
3172 * using DBUS interface: getPostCodesTS
3173 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003174inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003175{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003176 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003177 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003178 .methods(boost::beast::http::verb::get)(
3179 [&app](const crow::Request& req,
3180 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003181 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003182 {
3183 return;
3184 }
Ed Tanous14766872022-03-15 10:44:42 -07003185
Ed Tanous002d39b2022-05-31 08:59:27 -07003186 asyncResp->res.jsonValue["@odata.id"] =
3187 "/redfish/v1/Systems/system/LogServices/PostCodes";
3188 asyncResp->res.jsonValue["@odata.type"] =
3189 "#LogService.v1_1_0.LogService";
3190 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3191 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3192 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3193 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3194 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3195 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303196
Ed Tanous002d39b2022-05-31 08:59:27 -07003197 std::pair<std::string, std::string> redfishDateTimeOffset =
3198 crow::utility::getDateTimeOffsetNow();
3199 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3200 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3201 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303202
Ed Tanous002d39b2022-05-31 08:59:27 -07003203 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3204 {"target",
3205 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003206 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003207}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003208
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003209inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003210{
George Liu0fda0f12021-11-16 10:06:17 +08003211 BMCWEB_ROUTE(
3212 app,
3213 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003214 // The following privilege is incorrect; It should be ConfigureManager
3215 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003216 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003217 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003218 [&app](const crow::Request& req,
3219 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003220 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003221 {
3222 return;
3223 }
3224 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003225
Ed Tanous002d39b2022-05-31 08:59:27 -07003226 // Make call to post-code service to request clear all
3227 crow::connections::systemBus->async_method_call(
3228 [asyncResp](const boost::system::error_code ec) {
3229 if (ec)
3230 {
3231 // TODO Handle for specific error code
3232 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3233 << ec;
3234 asyncResp->res.result(
3235 boost::beast::http::status::internal_server_error);
3236 messages::internalError(asyncResp->res);
3237 return;
3238 }
3239 },
3240 "xyz.openbmc_project.State.Boot.PostCode0",
3241 "/xyz/openbmc_project/State/Boot/PostCode0",
3242 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3243 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003244}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003245
3246static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003247 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303248 const boost::container::flat_map<
3249 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003250 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3251 const uint64_t skip = 0, const uint64_t top = 0)
3252{
3253 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003254 const registries::Message* message =
3255 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003256
3257 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003258 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003259
3260 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303261 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3262 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003263 {
3264 currentCodeIndex++;
3265 std::string postcodeEntryID =
3266 "B" + std::to_string(bootIndex) + "-" +
3267 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3268
3269 uint64_t usecSinceEpoch = code.first;
3270 uint64_t usTimeOffset = 0;
3271
3272 if (1 == currentCodeIndex)
3273 { // already incremented
3274 firstCodeTimeUs = code.first;
3275 }
3276 else
3277 {
3278 usTimeOffset = code.first - firstCodeTimeUs;
3279 }
3280
3281 // skip if no specific codeIndex is specified and currentCodeIndex does
3282 // not fall between top and skip
3283 if ((codeIndex == 0) &&
3284 (currentCodeIndex <= skip || currentCodeIndex > top))
3285 {
3286 continue;
3287 }
3288
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003289 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003290 // currentIndex
3291 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3292 {
3293 // This is done for simplicity. 1st entry is needed to calculate
3294 // time offset. To improve efficiency, one can get to the entry
3295 // directly (possibly with flatmap's nth method)
3296 continue;
3297 }
3298
3299 // currentCodeIndex is within top and skip or equal to specified code
3300 // index
3301
3302 // Get the Created time from the timestamp
3303 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003304 entryTimeStr =
3305 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003306
3307 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3308 std::ostringstream hexCode;
3309 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303310 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003311 std::ostringstream timeOffsetStr;
3312 // Set Fixed -Point Notation
3313 timeOffsetStr << std::fixed;
3314 // Set precision to 4 digits
3315 timeOffsetStr << std::setprecision(4);
3316 // Add double to stream
3317 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3318 std::vector<std::string> messageArgs = {
3319 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3320
3321 // Get MessageArgs template from message registry
3322 std::string msg;
3323 if (message != nullptr)
3324 {
3325 msg = message->message;
3326
3327 // fill in this post code value
3328 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003329 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003330 {
3331 std::string argStr = "%" + std::to_string(++i);
3332 size_t argPos = msg.find(argStr);
3333 if (argPos != std::string::npos)
3334 {
3335 msg.replace(argPos, argStr.length(), messageArg);
3336 }
3337 }
3338 }
3339
Tim Leed4342a92020-04-27 11:47:58 +08003340 // Get Severity template from message registry
3341 std::string severity;
3342 if (message != nullptr)
3343 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003344 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003345 }
3346
ZhikuiRena3316fc2020-01-29 14:58:08 -08003347 // add to AsyncResp
3348 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003349 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills84afc482022-06-24 12:38:23 -07003350 bmcLogEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
3351 bmcLogEntry["@odata.id"] =
3352 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3353 postcodeEntryID;
3354 bmcLogEntry["Name"] = "POST Code Log Entry";
3355 bmcLogEntry["Id"] = postcodeEntryID;
3356 bmcLogEntry["Message"] = std::move(msg);
3357 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3358 bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3359 bmcLogEntry["EntryType"] = "Event";
3360 bmcLogEntry["Severity"] = std::move(severity);
3361 bmcLogEntry["Created"] = entryTimeStr;
George Liu647b3cd2021-07-05 12:43:56 +08003362 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3363 {
3364 bmcLogEntry["AdditionalDataURI"] =
3365 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3366 postcodeEntryID + "/attachment";
3367 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003368 }
3369}
3370
zhanghch058d1b46d2021-04-01 11:18:24 +08003371static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003372 const uint16_t bootIndex,
3373 const uint64_t codeIndex)
3374{
3375 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303376 [aResp, bootIndex,
3377 codeIndex](const boost::system::error_code ec,
3378 const boost::container::flat_map<
3379 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3380 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003381 if (ec)
3382 {
3383 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3384 messages::internalError(aResp->res);
3385 return;
3386 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003387
Ed Tanous002d39b2022-05-31 08:59:27 -07003388 // skip the empty postcode boots
3389 if (postcode.empty())
3390 {
3391 return;
3392 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003393
Ed Tanous002d39b2022-05-31 08:59:27 -07003394 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003395
Ed Tanous002d39b2022-05-31 08:59:27 -07003396 aResp->res.jsonValue["Members@odata.count"] =
3397 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003398 },
Jonathan Doman15124762021-01-07 17:54:17 -08003399 "xyz.openbmc_project.State.Boot.PostCode0",
3400 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003401 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3402 bootIndex);
3403}
3404
zhanghch058d1b46d2021-04-01 11:18:24 +08003405static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003406 const uint16_t bootIndex,
3407 const uint16_t bootCount,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003408 const uint64_t entryCount, size_t skip,
3409 size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003410{
3411 crow::connections::systemBus->async_method_call(
3412 [aResp, bootIndex, bootCount, entryCount, skip,
3413 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303414 const boost::container::flat_map<
3415 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3416 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003417 if (ec)
3418 {
3419 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3420 messages::internalError(aResp->res);
3421 return;
3422 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003423
Ed Tanous002d39b2022-05-31 08:59:27 -07003424 uint64_t endCount = entryCount;
3425 if (!postcode.empty())
3426 {
3427 endCount = entryCount + postcode.size();
Ed Tanous3648c8b2022-07-25 13:39:59 -07003428 if (skip < endCount && (top + skip) > entryCount)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003429 {
Ed Tanous3648c8b2022-07-25 13:39:59 -07003430 uint64_t thisBootSkip =
3431 std::max(static_cast<uint64_t>(skip), entryCount) -
3432 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003433 uint64_t thisBootTop =
Ed Tanous3648c8b2022-07-25 13:39:59 -07003434 std::min(static_cast<uint64_t>(top + skip), endCount) -
3435 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003436
3437 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3438 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003439 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003440 aResp->res.jsonValue["Members@odata.count"] = endCount;
3441 }
3442
3443 // continue to previous bootIndex
3444 if (bootIndex < bootCount)
3445 {
3446 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3447 bootCount, endCount, skip, top);
3448 }
Jiaqing Zhao81584ab2022-07-28 00:33:45 +08003449 else if (skip + top < endCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07003450 {
3451 aResp->res.jsonValue["Members@odata.nextLink"] =
3452 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3453 std::to_string(skip + top);
3454 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003455 },
Jonathan Doman15124762021-01-07 17:54:17 -08003456 "xyz.openbmc_project.State.Boot.PostCode0",
3457 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003458 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3459 bootIndex);
3460}
3461
zhanghch058d1b46d2021-04-01 11:18:24 +08003462static void
3463 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003464 size_t skip, size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003465{
3466 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003467 sdbusplus::asio::getProperty<uint16_t>(
3468 *crow::connections::systemBus,
3469 "xyz.openbmc_project.State.Boot.PostCode0",
3470 "/xyz/openbmc_project/State/Boot/PostCode0",
3471 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3472 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3473 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003474 if (ec)
3475 {
3476 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3477 messages::internalError(aResp->res);
3478 return;
3479 }
3480 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003481 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003482}
3483
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003484inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003485{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003486 BMCWEB_ROUTE(app,
3487 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003488 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003489 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003490 [&app](const crow::Request& req,
3491 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003492 query_param::QueryCapabilities capabilities = {
3493 .canDelegateTop = true,
3494 .canDelegateSkip = true,
3495 };
3496 query_param::Query delegatedQuery;
3497 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003498 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003499 {
3500 return;
3501 }
3502 asyncResp->res.jsonValue["@odata.type"] =
3503 "#LogEntryCollection.LogEntryCollection";
3504 asyncResp->res.jsonValue["@odata.id"] =
3505 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3506 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3507 asyncResp->res.jsonValue["Description"] =
3508 "Collection of POST Code Log Entries";
3509 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3510 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07003511 size_t skip = delegatedQuery.skip.value_or(0);
3512 size_t top =
3513 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
3514 getCurrentBootNumber(asyncResp, skip, top);
Ed Tanous002d39b2022-05-31 08:59:27 -07003515 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003516}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003517
George Liu647b3cd2021-07-05 12:43:56 +08003518/**
3519 * @brief Parse post code ID and get the current value and index value
3520 * eg: postCodeID=B1-2, currentValue=1, index=2
3521 *
3522 * @param[in] postCodeID Post Code ID
3523 * @param[out] currentValue Current value
3524 * @param[out] index Index value
3525 *
3526 * @return bool true if the parsing is successful, false the parsing fails
3527 */
3528inline static bool parsePostCode(const std::string& postCodeID,
3529 uint64_t& currentValue, uint16_t& index)
3530{
3531 std::vector<std::string> split;
3532 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3533 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3534 {
3535 return false;
3536 }
3537
Ed Tanousca45aa32022-01-07 09:28:45 -08003538 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003539 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003540 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003541 const char* end = split[0].data() + split[0].size();
3542 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3543
3544 if (ptrIndex != end || ecIndex != std::errc())
3545 {
3546 return false;
3547 }
3548
3549 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003550
3551 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003552 end = split[1].data() + split[1].size();
3553 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003554
Tony Lee517d9a52022-06-28 15:41:23 +08003555 return ptrValue == end && ecValue == std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003556}
3557
3558inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3559{
George Liu0fda0f12021-11-16 10:06:17 +08003560 BMCWEB_ROUTE(
3561 app,
3562 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003563 .privileges(redfish::privileges::getLogEntry)
3564 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003565 [&app](const crow::Request& req,
3566 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3567 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003568 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003569 {
3570 return;
3571 }
3572 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3573 {
3574 asyncResp->res.result(boost::beast::http::status::bad_request);
3575 return;
3576 }
George Liu647b3cd2021-07-05 12:43:56 +08003577
Ed Tanous002d39b2022-05-31 08:59:27 -07003578 uint64_t currentValue = 0;
3579 uint16_t index = 0;
3580 if (!parsePostCode(postCodeID, currentValue, index))
3581 {
3582 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3583 return;
3584 }
George Liu647b3cd2021-07-05 12:43:56 +08003585
Ed Tanous002d39b2022-05-31 08:59:27 -07003586 crow::connections::systemBus->async_method_call(
3587 [asyncResp, postCodeID, currentValue](
3588 const boost::system::error_code ec,
3589 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3590 postcodes) {
3591 if (ec.value() == EBADR)
3592 {
3593 messages::resourceNotFound(asyncResp->res, "LogEntry",
3594 postCodeID);
3595 return;
3596 }
3597 if (ec)
3598 {
3599 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3600 messages::internalError(asyncResp->res);
3601 return;
3602 }
George Liu647b3cd2021-07-05 12:43:56 +08003603
Ed Tanous002d39b2022-05-31 08:59:27 -07003604 size_t value = static_cast<size_t>(currentValue) - 1;
3605 if (value == std::string::npos || postcodes.size() < currentValue)
3606 {
3607 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3608 messages::resourceNotFound(asyncResp->res, "LogEntry",
3609 postCodeID);
3610 return;
3611 }
George Liu647b3cd2021-07-05 12:43:56 +08003612
Ed Tanous002d39b2022-05-31 08:59:27 -07003613 const auto& [tID, c] = postcodes[value];
3614 if (c.empty())
3615 {
3616 BMCWEB_LOG_INFO << "No found post code data";
3617 messages::resourceNotFound(asyncResp->res, "LogEntry",
3618 postCodeID);
3619 return;
3620 }
3621 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3622 const char* d = reinterpret_cast<const char*>(c.data());
3623 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003624
Ed Tanousd9f6c622022-03-17 09:12:17 -07003625 asyncResp->res.addHeader(boost::beast::http::field::content_type,
Ed Tanous002d39b2022-05-31 08:59:27 -07003626 "application/octet-stream");
Ed Tanousd9f6c622022-03-17 09:12:17 -07003627 asyncResp->res.addHeader(
3628 boost::beast::http::field::content_transfer_encoding, "Base64");
Ed Tanous002d39b2022-05-31 08:59:27 -07003629 asyncResp->res.body() = crow::utility::base64encode(strData);
3630 },
3631 "xyz.openbmc_project.State.Boot.PostCode0",
3632 "/xyz/openbmc_project/State/Boot/PostCode0",
3633 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3634 });
George Liu647b3cd2021-07-05 12:43:56 +08003635}
3636
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003637inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003638{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003639 BMCWEB_ROUTE(
3640 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003641 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003642 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003643 [&app](const crow::Request& req,
3644 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3645 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003646 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003647 {
3648 return;
3649 }
3650 uint16_t bootIndex = 0;
3651 uint64_t codeIndex = 0;
3652 if (!parsePostCode(targetID, codeIndex, bootIndex))
3653 {
3654 // Requested ID was not found
3655 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3656 return;
3657 }
3658 if (bootIndex == 0 || codeIndex == 0)
3659 {
3660 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3661 << targetID;
3662 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003663
Ed Tanous002d39b2022-05-31 08:59:27 -07003664 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3665 asyncResp->res.jsonValue["@odata.id"] =
3666 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3667 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3668 asyncResp->res.jsonValue["Description"] =
3669 "Collection of POST Code Log Entries";
3670 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3671 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003672
Ed Tanous002d39b2022-05-31 08:59:27 -07003673 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3674 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003675}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003676
Ed Tanous1da66f72018-07-27 16:13:37 -07003677} // namespace redfish