blob: 7d511e2bb1cc7d946f724289811d9db6825d9177 [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 }
728 }
729 else if (dumpType == "BMC")
730 {
731 if (!diagnosticDataType)
732 {
George Liu0fda0f12021-11-16 10:06:17 +0800733 BMCWEB_LOG_ERROR
734 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500735 messages::actionParameterMissing(
736 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
737 return;
738 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700739 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500740 {
741 BMCWEB_LOG_ERROR
742 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700743 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500744 return;
745 }
746 }
747
748 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700749 [asyncResp, payload(task::Payload(req)), dumpPath,
750 dumpType](const boost::system::error_code ec,
751 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700752 if (ec)
753 {
754 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
755 messages::internalError(asyncResp->res);
756 return;
757 }
758 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500759
Ed Tanous002d39b2022-05-31 08:59:27 -0700760 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
761 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500762 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500763 "xyz.openbmc_project.Dump.Manager",
764 "/xyz/openbmc_project/dump/" +
765 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500766 "xyz.openbmc_project.Dump.Create", "CreateDump");
767}
768
zhanghch058d1b46d2021-04-01 11:18:24 +0800769inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
770 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500771{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500772 std::string dumpTypeLowerCopy =
773 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800774
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500775 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800776 [asyncResp, dumpType](
777 const boost::system::error_code ec,
778 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700779 if (ec)
780 {
781 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
782 messages::internalError(asyncResp->res);
783 return;
784 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500785
Ed Tanous002d39b2022-05-31 08:59:27 -0700786 for (const std::string& path : subTreePaths)
787 {
788 sdbusplus::message::object_path objPath(path);
789 std::string logID = objPath.filename();
790 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500791 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700792 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500793 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700794 deleteDumpEntry(asyncResp, logID, dumpType);
795 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500796 },
797 "xyz.openbmc_project.ObjectMapper",
798 "/xyz/openbmc_project/object_mapper",
799 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500800 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
801 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
802 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500803}
804
Ed Tanousb9d36b42022-02-26 21:42:46 -0800805inline static void
806 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
807 std::string& filename, std::string& timestamp,
808 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700809{
810 for (auto property : params)
811 {
812 if (property.first == "Timestamp")
813 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500814 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500815 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700816 if (value != nullptr)
817 {
818 timestamp = *value;
819 }
820 }
821 else if (property.first == "Filename")
822 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500823 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500824 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700825 if (value != nullptr)
826 {
827 filename = *value;
828 }
829 }
830 else if (property.first == "Log")
831 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500832 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500833 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700834 if (value != nullptr)
835 {
836 logfile = *value;
837 }
838 }
839 }
840}
841
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500842constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700843inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700844{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800845 /**
846 * Functions triggers appropriate requests on DBus
847 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700848 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700849 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700850 .methods(boost::beast::http::verb::get)(
851 [&app](const crow::Request& req,
852 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000853 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700854 {
855 return;
856 }
857 // Collections don't include the static data added by SubRoute
858 // because it has a duplicate entry for members
859 asyncResp->res.jsonValue["@odata.type"] =
860 "#LogServiceCollection.LogServiceCollection";
861 asyncResp->res.jsonValue["@odata.id"] =
862 "/redfish/v1/Systems/system/LogServices";
863 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
864 asyncResp->res.jsonValue["Description"] =
865 "Collection of LogServices for this Computer System";
866 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
867 logServiceArray = nlohmann::json::array();
868 nlohmann::json::object_t eventLog;
869 eventLog["@odata.id"] =
870 "/redfish/v1/Systems/system/LogServices/EventLog";
871 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500872#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700873 nlohmann::json::object_t dumpLog;
874 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
875 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600876#endif
877
Jason M. Billsd53dd412019-02-12 17:16:22 -0800878#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700879 nlohmann::json::object_t crashdump;
880 crashdump["@odata.id"] =
881 "/redfish/v1/Systems/system/LogServices/Crashdump";
882 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800883#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800884
885#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700886 nlohmann::json::object_t hostlogger;
887 hostlogger["@odata.id"] =
888 "/redfish/v1/Systems/system/LogServices/HostLogger";
889 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800890#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700891 asyncResp->res.jsonValue["Members@odata.count"] =
892 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800893
Ed Tanous002d39b2022-05-31 08:59:27 -0700894 crow::connections::systemBus->async_method_call(
895 [asyncResp](const boost::system::error_code ec,
896 const dbus::utility::MapperGetSubTreePathsResponse&
897 subtreePath) {
898 if (ec)
899 {
900 BMCWEB_LOG_ERROR << ec;
901 return;
902 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700903
Ed Tanous002d39b2022-05-31 08:59:27 -0700904 for (const auto& pathStr : subtreePath)
905 {
906 if (pathStr.find("PostCode") != std::string::npos)
907 {
908 nlohmann::json& logServiceArrayLocal =
909 asyncResp->res.jsonValue["Members"];
910 logServiceArrayLocal.push_back(
911 {{"@odata.id",
912 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
913 asyncResp->res.jsonValue["Members@odata.count"] =
914 logServiceArrayLocal.size();
915 return;
916 }
917 }
918 },
919 "xyz.openbmc_project.ObjectMapper",
920 "/xyz/openbmc_project/object_mapper",
921 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
922 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700923 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700924}
925
926inline void requestRoutesEventLogService(App& app)
927{
928 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700929 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700930 .methods(boost::beast::http::verb::get)(
931 [&app](const crow::Request& req,
932 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000933 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700934 {
935 return;
936 }
937 asyncResp->res.jsonValue["@odata.id"] =
938 "/redfish/v1/Systems/system/LogServices/EventLog";
939 asyncResp->res.jsonValue["@odata.type"] =
940 "#LogService.v1_1_0.LogService";
941 asyncResp->res.jsonValue["Name"] = "Event Log Service";
942 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
943 asyncResp->res.jsonValue["Id"] = "EventLog";
944 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530945
Ed Tanous002d39b2022-05-31 08:59:27 -0700946 std::pair<std::string, std::string> redfishDateTimeOffset =
947 crow::utility::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530948
Ed Tanous002d39b2022-05-31 08:59:27 -0700949 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
950 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
951 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +0530952
Ed Tanous002d39b2022-05-31 08:59:27 -0700953 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
954 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
955 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700956
Ed Tanous002d39b2022-05-31 08:59:27 -0700957 {"target",
958 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700959 });
960}
961
962inline void requestRoutesJournalEventLogClear(App& app)
963{
Jason M. Bills4978b632022-02-22 14:17:43 -0800964 BMCWEB_ROUTE(
965 app,
966 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -0700967 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700968 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -0700969 [&app](const crow::Request& req,
970 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000971 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700972 {
973 return;
974 }
975 // Clear the EventLog by deleting the log files
976 std::vector<std::filesystem::path> redfishLogFiles;
977 if (getRedfishLogFiles(redfishLogFiles))
978 {
979 for (const std::filesystem::path& file : redfishLogFiles)
980 {
981 std::error_code ec;
982 std::filesystem::remove(file, ec);
983 }
984 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800985
Ed Tanous002d39b2022-05-31 08:59:27 -0700986 // Reload rsyslog so it knows to start new log files
987 crow::connections::systemBus->async_method_call(
988 [asyncResp](const boost::system::error_code ec) {
989 if (ec)
990 {
991 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
992 messages::internalError(asyncResp->res);
993 return;
994 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800995
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 messages::success(asyncResp->res);
997 },
998 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
999 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1000 "replace");
1001 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001002}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001003
Jason M. Billsac992cd2022-06-24 13:31:46 -07001004enum class LogParseError
1005{
1006 success,
1007 parseFailed,
1008 messageIdNotInRegistry,
1009};
1010
1011static LogParseError
1012 fillEventLogEntryJson(const std::string& logEntryID,
1013 const std::string& logEntry,
1014 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001015{
Jason M. Bills95820182019-04-22 16:25:34 -07001016 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001017 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001018 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001019 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001020 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001021 return LogParseError::parseFailed;
Jason M. Bills95820182019-04-22 16:25:34 -07001022 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001023 std::string timestamp = logEntry.substr(0, space);
1024 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001025 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001026 if (entryStart == std::string::npos)
1027 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001028 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001029 }
1030 std::string_view entry(logEntry);
1031 entry.remove_prefix(entryStart);
1032 // Use split to separate the entry into its fields
1033 std::vector<std::string> logEntryFields;
1034 boost::split(logEntryFields, entry, boost::is_any_of(","),
1035 boost::token_compress_on);
1036 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001037 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001038 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001039 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001040 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001041 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001042
Jason M. Bills4851d452019-03-28 11:27:48 -07001043 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001044 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001045
Sui Chen54417b02022-03-24 14:59:52 -07001046 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001047 {
Sui Chen54417b02022-03-24 14:59:52 -07001048 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001049 return LogParseError::messageIdNotInRegistry;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001050 }
1051
Sui Chen54417b02022-03-24 14:59:52 -07001052 std::string msg = message->message;
1053
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001054 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001055 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001056 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001057 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001058 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001059 // If the first string is empty, assume there are no MessageArgs
1060 std::size_t messageArgsSize = 0;
1061 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001062 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001063 messageArgsSize = logEntryFields.size() - 1;
1064 }
1065
Ed Tanous23a21a12020-07-25 04:45:05 +00001066 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001067
1068 // Fill the MessageArgs into the Message
1069 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001070 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001071 {
1072 std::string argStr = "%" + std::to_string(++i);
1073 size_t argPos = msg.find(argStr);
1074 if (argPos != std::string::npos)
1075 {
1076 msg.replace(argPos, argStr.length(), messageArg);
1077 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001078 }
1079 }
1080
Jason M. Bills95820182019-04-22 16:25:34 -07001081 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1082 // format which matches the Redfish format except for the fractional seconds
1083 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001084 std::size_t dot = timestamp.find_first_of('.');
1085 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001086 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001087 {
Jason M. Bills95820182019-04-22 16:25:34 -07001088 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001089 }
1090
1091 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07001092 logEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1093 logEntryJson["@odata.id"] =
1094 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1095 logEntryJson["Name"] = "System Event Log Entry";
1096 logEntryJson["Id"] = logEntryID;
1097 logEntryJson["Message"] = std::move(msg);
1098 logEntryJson["MessageId"] = std::move(messageID);
1099 logEntryJson["MessageArgs"] = messageArgs;
1100 logEntryJson["EntryType"] = "Event";
1101 logEntryJson["Severity"] = message->messageSeverity;
1102 logEntryJson["Created"] = std::move(timestamp);
Jason M. Billsac992cd2022-06-24 13:31:46 -07001103 return LogParseError::success;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001104}
1105
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001106inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001107{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001108 BMCWEB_ROUTE(app,
1109 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001110 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001111 .methods(boost::beast::http::verb::get)(
1112 [&app](const crow::Request& req,
1113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1114 query_param::QueryCapabilities capabilities = {
1115 .canDelegateTop = true,
1116 .canDelegateSkip = true,
1117 };
1118 query_param::Query delegatedQuery;
1119 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001120 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001121 {
1122 return;
1123 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001124 size_t top =
1125 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
1126 size_t skip = delegatedQuery.skip.value_or(0);
1127
Ed Tanous002d39b2022-05-31 08:59:27 -07001128 // Collections don't include the static data added by SubRoute
1129 // because it has a duplicate entry for members
1130 asyncResp->res.jsonValue["@odata.type"] =
1131 "#LogEntryCollection.LogEntryCollection";
1132 asyncResp->res.jsonValue["@odata.id"] =
1133 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1134 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1135 asyncResp->res.jsonValue["Description"] =
1136 "Collection of System Event Log Entries";
1137
1138 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1139 logEntryArray = nlohmann::json::array();
1140 // Go through the log files and create a unique ID for each
1141 // entry
1142 std::vector<std::filesystem::path> redfishLogFiles;
1143 getRedfishLogFiles(redfishLogFiles);
1144 uint64_t entryCount = 0;
1145 std::string logEntry;
1146
1147 // Oldest logs are in the last file, so start there and loop
1148 // backwards
1149 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1150 it++)
1151 {
1152 std::ifstream logStream(*it);
1153 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001154 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001155 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001156 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001157
Ed Tanous002d39b2022-05-31 08:59:27 -07001158 // Reset the unique ID on the first entry
1159 bool firstEntry = true;
1160 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001161 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001162 std::string idStr;
1163 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001164 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001165 continue;
1166 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001167 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001168
Jason M. Billsde703c52022-06-23 14:19:04 -07001169 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001170 LogParseError status =
1171 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1172 if (status == LogParseError::messageIdNotInRegistry)
1173 {
1174 continue;
1175 }
1176 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001177 {
1178 messages::internalError(asyncResp->res);
1179 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001180 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001181
Jason M. Billsde703c52022-06-23 14:19:04 -07001182 entryCount++;
1183 // Handle paging using skip (number of entries to skip from the
1184 // start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07001185 if (entryCount <= skip || entryCount > skip + top)
Jason M. Billsde703c52022-06-23 14:19:04 -07001186 {
1187 continue;
1188 }
1189
1190 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001191 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001192 }
1193 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001194 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07001195 {
1196 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1197 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001198 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07001199 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001200 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001201}
Chicago Duan336e96c2019-07-15 14:22:08 +08001202
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001203inline void requestRoutesJournalEventLogEntry(App& app)
1204{
1205 BMCWEB_ROUTE(
1206 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001207 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001208 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001209 [&app](const crow::Request& req,
1210 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1211 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001212 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001213 {
1214 return;
1215 }
1216 const std::string& targetID = param;
1217
1218 // Go through the log files and check the unique ID for each
1219 // entry to find the target entry
1220 std::vector<std::filesystem::path> redfishLogFiles;
1221 getRedfishLogFiles(redfishLogFiles);
1222 std::string logEntry;
1223
1224 // Oldest logs are in the last file, so start there and loop
1225 // backwards
1226 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1227 it++)
1228 {
1229 std::ifstream logStream(*it);
1230 if (!logStream.is_open())
1231 {
1232 continue;
1233 }
1234
1235 // Reset the unique ID on the first entry
1236 bool firstEntry = true;
1237 while (std::getline(logStream, logEntry))
1238 {
1239 std::string idStr;
1240 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001241 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001242 continue;
1243 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001244 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001245
1246 if (idStr == targetID)
1247 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001248 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001249 LogParseError status =
1250 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1251 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001252 {
1253 messages::internalError(asyncResp->res);
1254 return;
1255 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07001256 asyncResp->res.jsonValue.update(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001257 return;
1258 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001259 }
1260 }
1261 // Requested ID was not found
1262 messages::resourceMissingAtURI(asyncResp->res,
1263 crow::utility::urlFromPieces(targetID));
1264 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001265}
1266
1267inline void requestRoutesDBusEventLogEntryCollection(App& app)
1268{
1269 BMCWEB_ROUTE(app,
1270 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001271 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001272 .methods(boost::beast::http::verb::get)(
1273 [&app](const crow::Request& req,
1274 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001275 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001276 {
1277 return;
1278 }
1279 // Collections don't include the static data added by SubRoute
1280 // because it has a duplicate entry for members
1281 asyncResp->res.jsonValue["@odata.type"] =
1282 "#LogEntryCollection.LogEntryCollection";
1283 asyncResp->res.jsonValue["@odata.id"] =
1284 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1285 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1286 asyncResp->res.jsonValue["Description"] =
1287 "Collection of System Event Log Entries";
1288
1289 // DBus implementation of EventLog/Entries
1290 // Make call to Logging Service to find all log entry objects
1291 crow::connections::systemBus->async_method_call(
1292 [asyncResp](const boost::system::error_code ec,
1293 const dbus::utility::ManagedObjectType& resp) {
1294 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001295 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001296 // TODO Handle for specific error code
1297 BMCWEB_LOG_ERROR
1298 << "getLogEntriesIfaceData resp_handler got error " << ec;
1299 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001300 return;
1301 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001302 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1303 entriesArray = nlohmann::json::array();
1304 for (const auto& objectPath : resp)
1305 {
1306 const uint32_t* id = nullptr;
1307 const uint64_t* timestamp = nullptr;
1308 const uint64_t* updateTimestamp = nullptr;
1309 const std::string* severity = nullptr;
1310 const std::string* message = nullptr;
1311 const std::string* filePath = nullptr;
1312 bool resolved = false;
1313 for (const auto& interfaceMap : objectPath.second)
1314 {
1315 if (interfaceMap.first ==
1316 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001317 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001318 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001319 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001320 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001321 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001322 id = std::get_if<uint32_t>(&propertyMap.second);
1323 }
1324 else if (propertyMap.first == "Timestamp")
1325 {
1326 timestamp =
1327 std::get_if<uint64_t>(&propertyMap.second);
1328 }
1329 else if (propertyMap.first == "UpdateTimestamp")
1330 {
1331 updateTimestamp =
1332 std::get_if<uint64_t>(&propertyMap.second);
1333 }
1334 else if (propertyMap.first == "Severity")
1335 {
1336 severity = std::get_if<std::string>(
1337 &propertyMap.second);
1338 }
1339 else if (propertyMap.first == "Message")
1340 {
1341 message = std::get_if<std::string>(
1342 &propertyMap.second);
1343 }
1344 else if (propertyMap.first == "Resolved")
1345 {
1346 const bool* resolveptr =
1347 std::get_if<bool>(&propertyMap.second);
1348 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001349 {
1350 messages::internalError(asyncResp->res);
1351 return;
1352 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001353 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001354 }
1355 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001356 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001357 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001358 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001359 messages::internalError(asyncResp->res);
1360 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001361 }
1362 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001363 else if (interfaceMap.first ==
1364 "xyz.openbmc_project.Common.FilePath")
1365 {
1366 for (const auto& propertyMap : interfaceMap.second)
1367 {
1368 if (propertyMap.first == "Path")
1369 {
1370 filePath = std::get_if<std::string>(
1371 &propertyMap.second);
1372 }
1373 }
1374 }
1375 }
1376 // Object path without the
1377 // xyz.openbmc_project.Logging.Entry interface, ignore
1378 // and continue.
1379 if (id == nullptr || message == nullptr ||
1380 severity == nullptr || timestamp == nullptr ||
1381 updateTimestamp == nullptr)
1382 {
1383 continue;
1384 }
1385 entriesArray.push_back({});
1386 nlohmann::json& thisEntry = entriesArray.back();
1387 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1388 thisEntry["@odata.id"] =
1389 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1390 std::to_string(*id);
1391 thisEntry["Name"] = "System Event Log Entry";
1392 thisEntry["Id"] = std::to_string(*id);
1393 thisEntry["Message"] = *message;
1394 thisEntry["Resolved"] = resolved;
1395 thisEntry["EntryType"] = "Event";
1396 thisEntry["Severity"] =
1397 translateSeverityDbusToRedfish(*severity);
1398 thisEntry["Created"] =
1399 crow::utility::getDateTimeUintMs(*timestamp);
1400 thisEntry["Modified"] =
1401 crow::utility::getDateTimeUintMs(*updateTimestamp);
1402 if (filePath != nullptr)
1403 {
1404 thisEntry["AdditionalDataURI"] =
1405 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1406 std::to_string(*id) + "/attachment";
1407 }
1408 }
1409 std::sort(
1410 entriesArray.begin(), entriesArray.end(),
1411 [](const nlohmann::json& left, const nlohmann::json& right) {
1412 return (left["Id"] <= right["Id"]);
1413 });
1414 asyncResp->res.jsonValue["Members@odata.count"] =
1415 entriesArray.size();
1416 },
1417 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1418 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001419 });
1420}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001421
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001422inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001423{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001424 BMCWEB_ROUTE(
1425 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001426 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001427 .methods(boost::beast::http::verb::get)(
1428 [&app](const crow::Request& req,
1429 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1430 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001431 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001432 {
1433 return;
1434 }
1435 std::string entryID = param;
1436 dbus::utility::escapePathForDbus(entryID);
1437
1438 // DBus implementation of EventLog/Entries
1439 // Make call to Logging Service to find all log entry objects
1440 crow::connections::systemBus->async_method_call(
1441 [asyncResp, entryID](const boost::system::error_code ec,
1442 const dbus::utility::DBusPropertiesMap& resp) {
1443 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001444 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001445 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1446 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001447 return;
1448 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001449 if (ec)
1450 {
1451 BMCWEB_LOG_ERROR
1452 << "EventLogEntry (DBus) resp_handler got error " << ec;
1453 messages::internalError(asyncResp->res);
1454 return;
1455 }
1456 const uint32_t* id = nullptr;
1457 const uint64_t* timestamp = nullptr;
1458 const uint64_t* updateTimestamp = nullptr;
1459 const std::string* severity = nullptr;
1460 const std::string* message = nullptr;
1461 const std::string* filePath = nullptr;
1462 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001463
Ed Tanous002d39b2022-05-31 08:59:27 -07001464 for (const auto& propertyMap : resp)
1465 {
1466 if (propertyMap.first == "Id")
1467 {
1468 id = std::get_if<uint32_t>(&propertyMap.second);
1469 }
1470 else if (propertyMap.first == "Timestamp")
1471 {
1472 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1473 }
1474 else if (propertyMap.first == "UpdateTimestamp")
1475 {
1476 updateTimestamp =
1477 std::get_if<uint64_t>(&propertyMap.second);
1478 }
1479 else if (propertyMap.first == "Severity")
1480 {
1481 severity = std::get_if<std::string>(&propertyMap.second);
1482 }
1483 else if (propertyMap.first == "Message")
1484 {
1485 message = std::get_if<std::string>(&propertyMap.second);
1486 }
1487 else if (propertyMap.first == "Resolved")
1488 {
1489 const bool* resolveptr =
1490 std::get_if<bool>(&propertyMap.second);
1491 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001492 {
1493 messages::internalError(asyncResp->res);
1494 return;
1495 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001496 resolved = *resolveptr;
1497 }
1498 else if (propertyMap.first == "Path")
1499 {
1500 filePath = std::get_if<std::string>(&propertyMap.second);
1501 }
1502 }
1503 if (id == nullptr || message == nullptr || severity == nullptr ||
1504 timestamp == nullptr || updateTimestamp == nullptr)
1505 {
1506 messages::internalError(asyncResp->res);
1507 return;
1508 }
1509 asyncResp->res.jsonValue["@odata.type"] =
1510 "#LogEntry.v1_8_0.LogEntry";
1511 asyncResp->res.jsonValue["@odata.id"] =
1512 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1513 std::to_string(*id);
1514 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1515 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1516 asyncResp->res.jsonValue["Message"] = *message;
1517 asyncResp->res.jsonValue["Resolved"] = resolved;
1518 asyncResp->res.jsonValue["EntryType"] = "Event";
1519 asyncResp->res.jsonValue["Severity"] =
1520 translateSeverityDbusToRedfish(*severity);
1521 asyncResp->res.jsonValue["Created"] =
1522 crow::utility::getDateTimeUintMs(*timestamp);
1523 asyncResp->res.jsonValue["Modified"] =
1524 crow::utility::getDateTimeUintMs(*updateTimestamp);
1525 if (filePath != nullptr)
1526 {
1527 asyncResp->res.jsonValue["AdditionalDataURI"] =
1528 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1529 std::to_string(*id) + "/attachment";
1530 }
1531 },
1532 "xyz.openbmc_project.Logging",
1533 "/xyz/openbmc_project/logging/entry/" + entryID,
1534 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001535 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001536
1537 BMCWEB_ROUTE(
1538 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001539 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001540 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001541 [&app](const crow::Request& req,
1542 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1543 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001544 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001545 {
1546 return;
1547 }
1548 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001549
Ed Tanous002d39b2022-05-31 08:59:27 -07001550 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1551 resolved))
1552 {
1553 return;
1554 }
1555 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001556
Ed Tanous002d39b2022-05-31 08:59:27 -07001557 crow::connections::systemBus->async_method_call(
1558 [asyncResp, entryId](const boost::system::error_code ec) {
1559 if (ec)
1560 {
1561 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1562 messages::internalError(asyncResp->res);
1563 return;
1564 }
1565 },
1566 "xyz.openbmc_project.Logging",
1567 "/xyz/openbmc_project/logging/entry/" + entryId,
1568 "org.freedesktop.DBus.Properties", "Set",
1569 "xyz.openbmc_project.Logging.Entry", "Resolved",
1570 dbus::utility::DbusVariantType(*resolved));
1571 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001572
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001573 BMCWEB_ROUTE(
1574 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001575 .privileges(redfish::privileges::deleteLogEntry)
1576
Ed Tanous002d39b2022-05-31 08:59:27 -07001577 .methods(boost::beast::http::verb::delete_)(
1578 [&app](const crow::Request& req,
1579 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1580 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001581 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001582 {
1583 return;
1584 }
1585 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1586
1587 std::string entryID = param;
1588
1589 dbus::utility::escapePathForDbus(entryID);
1590
1591 // Process response from Logging service.
1592 auto respHandler =
1593 [asyncResp, entryID](const boost::system::error_code ec) {
1594 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1595 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001596 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001597 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001598 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001599 messages::resourceNotFound(asyncResp->res, "LogEntry",
1600 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001601 return;
1602 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001603 // TODO Handle for specific error code
1604 BMCWEB_LOG_ERROR
1605 << "EventLogEntry (DBus) doDelete respHandler got error "
1606 << ec;
1607 asyncResp->res.result(
1608 boost::beast::http::status::internal_server_error);
1609 return;
1610 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001611
Ed Tanous002d39b2022-05-31 08:59:27 -07001612 asyncResp->res.result(boost::beast::http::status::ok);
1613 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001614
Ed Tanous002d39b2022-05-31 08:59:27 -07001615 // Make call to Logging service to request Delete Log
1616 crow::connections::systemBus->async_method_call(
1617 respHandler, "xyz.openbmc_project.Logging",
1618 "/xyz/openbmc_project/logging/entry/" + entryID,
1619 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001620 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001621}
1622
1623inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001624{
George Liu0fda0f12021-11-16 10:06:17 +08001625 BMCWEB_ROUTE(
1626 app,
1627 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001628 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001629 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001630 [&app](const crow::Request& req,
1631 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1632 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001633 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001634 {
1635 return;
1636 }
1637 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1638 {
1639 asyncResp->res.result(boost::beast::http::status::bad_request);
1640 return;
1641 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001642
Ed Tanous002d39b2022-05-31 08:59:27 -07001643 std::string entryID = param;
1644 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001645
Ed Tanous002d39b2022-05-31 08:59:27 -07001646 crow::connections::systemBus->async_method_call(
1647 [asyncResp, entryID](const boost::system::error_code ec,
1648 const sdbusplus::message::unix_fd& unixfd) {
1649 if (ec.value() == EBADR)
1650 {
1651 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1652 entryID);
1653 return;
1654 }
1655 if (ec)
1656 {
1657 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1658 messages::internalError(asyncResp->res);
1659 return;
1660 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001661
Ed Tanous002d39b2022-05-31 08:59:27 -07001662 int fd = -1;
1663 fd = dup(unixfd);
1664 if (fd == -1)
1665 {
1666 messages::internalError(asyncResp->res);
1667 return;
1668 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001669
Ed Tanous002d39b2022-05-31 08:59:27 -07001670 long long int size = lseek(fd, 0, SEEK_END);
1671 if (size == -1)
1672 {
1673 messages::internalError(asyncResp->res);
1674 return;
1675 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001676
Ed Tanous002d39b2022-05-31 08:59:27 -07001677 // Arbitrary max size of 64kb
1678 constexpr int maxFileSize = 65536;
1679 if (size > maxFileSize)
1680 {
1681 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1682 << maxFileSize;
1683 messages::internalError(asyncResp->res);
1684 return;
1685 }
1686 std::vector<char> data(static_cast<size_t>(size));
1687 long long int rc = lseek(fd, 0, SEEK_SET);
1688 if (rc == -1)
1689 {
1690 messages::internalError(asyncResp->res);
1691 return;
1692 }
1693 rc = read(fd, data.data(), data.size());
1694 if ((rc == -1) || (rc != size))
1695 {
1696 messages::internalError(asyncResp->res);
1697 return;
1698 }
1699 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001700
Ed Tanous002d39b2022-05-31 08:59:27 -07001701 std::string_view strData(data.data(), data.size());
1702 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001703
Ed Tanous002d39b2022-05-31 08:59:27 -07001704 asyncResp->res.addHeader("Content-Type",
1705 "application/octet-stream");
1706 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
1707 asyncResp->res.body() = std::move(output);
1708 },
1709 "xyz.openbmc_project.Logging",
1710 "/xyz/openbmc_project/logging/entry/" + entryID,
1711 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1712 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001713}
1714
Spencer Kub7028eb2021-10-26 15:27:35 +08001715constexpr const char* hostLoggerFolderPath = "/var/log/console";
1716
1717inline bool
1718 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1719 std::vector<std::filesystem::path>& hostLoggerFiles)
1720{
1721 std::error_code ec;
1722 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1723 if (ec)
1724 {
1725 BMCWEB_LOG_ERROR << ec.message();
1726 return false;
1727 }
1728 for (const std::filesystem::directory_entry& it : logPath)
1729 {
1730 std::string filename = it.path().filename();
1731 // Prefix of each log files is "log". Find the file and save the
1732 // path
Ed Tanous11ba3972022-07-11 09:50:41 -07001733 if (filename.starts_with("log"))
Spencer Kub7028eb2021-10-26 15:27:35 +08001734 {
1735 hostLoggerFiles.emplace_back(it.path());
1736 }
1737 }
1738 // As the log files rotate, they are appended with a ".#" that is higher for
1739 // the older logs. Since we start from oldest logs, sort the name in
1740 // descending order.
1741 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1742 AlphanumLess<std::string>());
1743
1744 return true;
1745}
1746
Ed Tanous02cad962022-06-30 16:50:15 -07001747inline bool getHostLoggerEntries(
1748 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1749 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001750{
1751 GzFileReader logFile;
1752
1753 // Go though all log files and expose host logs.
1754 for (const std::filesystem::path& it : hostLoggerFiles)
1755 {
1756 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1757 {
1758 BMCWEB_LOG_ERROR << "fail to expose host logs";
1759 return false;
1760 }
1761 }
1762 // Get lastMessage from constructor by getter
1763 std::string lastMessage = logFile.getLastMessage();
1764 if (!lastMessage.empty())
1765 {
1766 logCount++;
1767 if (logCount > skip && logCount <= (skip + top))
1768 {
1769 logEntries.push_back(lastMessage);
1770 }
1771 }
1772 return true;
1773}
1774
1775inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1776 const std::string& msg,
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001777 nlohmann::json::object_t& logEntryJson)
Spencer Kub7028eb2021-10-26 15:27:35 +08001778{
1779 // Fill in the log entry with the gathered data.
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001780 logEntryJson["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
1781 logEntryJson["@odata.id"] =
1782 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1783 logEntryID;
1784 logEntryJson["Name"] = "Host Logger Entry";
1785 logEntryJson["Id"] = logEntryID;
1786 logEntryJson["Message"] = msg;
1787 logEntryJson["EntryType"] = "Oem";
1788 logEntryJson["Severity"] = "OK";
1789 logEntryJson["OemRecordFormat"] = "Host Logger Entry";
Spencer Kub7028eb2021-10-26 15:27:35 +08001790}
1791
1792inline void requestRoutesSystemHostLogger(App& app)
1793{
1794 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1795 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001796 .methods(boost::beast::http::verb::get)(
1797 [&app](const crow::Request& req,
1798 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001799 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001800 {
1801 return;
1802 }
1803 asyncResp->res.jsonValue["@odata.id"] =
1804 "/redfish/v1/Systems/system/LogServices/HostLogger";
1805 asyncResp->res.jsonValue["@odata.type"] =
1806 "#LogService.v1_1_0.LogService";
1807 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1808 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1809 asyncResp->res.jsonValue["Id"] = "HostLogger";
1810 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1811 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1812 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001813}
1814
1815inline void requestRoutesSystemHostLoggerCollection(App& app)
1816{
1817 BMCWEB_ROUTE(app,
1818 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1819 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001820 .methods(boost::beast::http::verb::get)(
1821 [&app](const crow::Request& req,
1822 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1823 query_param::QueryCapabilities capabilities = {
1824 .canDelegateTop = true,
1825 .canDelegateSkip = true,
1826 };
1827 query_param::Query delegatedQuery;
1828 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001829 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001830 {
1831 return;
1832 }
1833 asyncResp->res.jsonValue["@odata.id"] =
1834 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1835 asyncResp->res.jsonValue["@odata.type"] =
1836 "#LogEntryCollection.LogEntryCollection";
1837 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1838 asyncResp->res.jsonValue["Description"] =
1839 "Collection of HostLogger Entries";
1840 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1841 logEntryArray = nlohmann::json::array();
1842 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001843
Ed Tanous002d39b2022-05-31 08:59:27 -07001844 std::vector<std::filesystem::path> hostLoggerFiles;
1845 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1846 {
1847 BMCWEB_LOG_ERROR << "fail to get host log file path";
1848 return;
1849 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07001850 // If we weren't provided top and skip limits, use the defaults.
1851 size_t skip = delegatedQuery.skip.value_or(0);
1852 size_t top =
1853 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
Ed Tanous002d39b2022-05-31 08:59:27 -07001854 size_t logCount = 0;
1855 // This vector only store the entries we want to expose that
1856 // control by skip and top.
1857 std::vector<std::string> logEntries;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001858 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1859 logCount))
Ed Tanous002d39b2022-05-31 08:59:27 -07001860 {
1861 messages::internalError(asyncResp->res);
1862 return;
1863 }
1864 // If vector is empty, that means skip value larger than total
1865 // log count
1866 if (logEntries.empty())
1867 {
1868 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1869 return;
1870 }
1871 if (!logEntries.empty())
1872 {
1873 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001874 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001875 nlohmann::json::object_t hostLogEntry;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001876 fillHostLoggerEntryJson(std::to_string(skip + i), logEntries[i],
1877 hostLogEntry);
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001878 logEntryArray.push_back(std::move(hostLogEntry));
George Liu0fda0f12021-11-16 10:06:17 +08001879 }
1880
Ed Tanous002d39b2022-05-31 08:59:27 -07001881 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001882 if (skip + top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001883 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001884 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1885 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07001886 std::to_string(skip + top);
George Liu0fda0f12021-11-16 10:06:17 +08001887 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001888 }
George Liu0fda0f12021-11-16 10:06:17 +08001889 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001890}
1891
1892inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1893{
1894 BMCWEB_ROUTE(
1895 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1896 .privileges(redfish::privileges::getLogEntry)
1897 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001898 [&app](const crow::Request& req,
1899 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1900 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001901 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001902 {
1903 return;
1904 }
1905 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001906
Ed Tanous002d39b2022-05-31 08:59:27 -07001907 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001908
Ed Tanous002d39b2022-05-31 08:59:27 -07001909 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1910 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001911
Ed Tanous002d39b2022-05-31 08:59:27 -07001912 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1913 if (ec == std::errc::invalid_argument)
1914 {
1915 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1916 return;
1917 }
1918 if (ec == std::errc::result_out_of_range)
1919 {
1920 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1921 return;
1922 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001923
Ed Tanous002d39b2022-05-31 08:59:27 -07001924 std::vector<std::filesystem::path> hostLoggerFiles;
1925 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1926 {
1927 BMCWEB_LOG_ERROR << "fail to get host log file path";
1928 return;
1929 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001930
Ed Tanous002d39b2022-05-31 08:59:27 -07001931 size_t logCount = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07001932 size_t top = 1;
Ed Tanous002d39b2022-05-31 08:59:27 -07001933 std::vector<std::string> logEntries;
1934 // We can get specific entry by skip and top. For example, if we
1935 // want to get nth entry, we can set skip = n-1 and top = 1 to
1936 // get that entry
1937 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1938 logCount))
1939 {
1940 messages::internalError(asyncResp->res);
1941 return;
1942 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001943
Ed Tanous002d39b2022-05-31 08:59:27 -07001944 if (!logEntries.empty())
1945 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001946 nlohmann::json::object_t hostLogEntry;
1947 fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
1948 asyncResp->res.jsonValue.update(hostLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07001949 return;
1950 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001951
Ed Tanous002d39b2022-05-31 08:59:27 -07001952 // Requested ID was not found
1953 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1954 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001955}
1956
Claire Weinanfdd26902022-03-01 14:18:25 -08001957constexpr char const* dumpManagerIface =
1958 "xyz.openbmc_project.Collection.DeleteAll";
1959inline void handleLogServicesCollectionGet(
1960 crow::App& app, const crow::Request& req,
1961 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1962{
1963 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1964 {
1965 return;
1966 }
1967 // Collections don't include the static data added by SubRoute
1968 // because it has a duplicate entry for members
1969 asyncResp->res.jsonValue["@odata.type"] =
1970 "#LogServiceCollection.LogServiceCollection";
1971 asyncResp->res.jsonValue["@odata.id"] =
1972 "/redfish/v1/Managers/bmc/LogServices";
1973 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1974 asyncResp->res.jsonValue["Description"] =
1975 "Collection of LogServices for this Manager";
1976 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
1977 logServiceArray = nlohmann::json::array();
1978
1979#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1980 logServiceArray.push_back(
1981 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
1982#endif
1983
1984 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
1985
1986#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
1987 auto respHandler =
1988 [asyncResp](
1989 const boost::system::error_code ec,
1990 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
1991 if (ec)
1992 {
1993 BMCWEB_LOG_ERROR
1994 << "handleLogServicesCollectionGet respHandler got error "
1995 << ec;
1996 // Assume that getting an error simply means there are no dump
1997 // LogServices. Return without adding any error response.
1998 return;
1999 }
2000
2001 nlohmann::json& logServiceArrayLocal =
2002 asyncResp->res.jsonValue["Members"];
2003
2004 for (const std::string& path : subTreePaths)
2005 {
2006 if (path == "/xyz/openbmc_project/dump/bmc")
2007 {
2008 logServiceArrayLocal.push_back(
2009 {{"@odata.id",
2010 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
2011 }
2012 else if (path == "/xyz/openbmc_project/dump/faultlog")
2013 {
2014 logServiceArrayLocal.push_back(
2015 {{"@odata.id",
2016 "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
2017 }
2018 }
2019
2020 asyncResp->res.jsonValue["Members@odata.count"] =
2021 logServiceArrayLocal.size();
2022 };
2023
2024 crow::connections::systemBus->async_method_call(
2025 respHandler, "xyz.openbmc_project.ObjectMapper",
2026 "/xyz/openbmc_project/object_mapper",
2027 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2028 "/xyz/openbmc_project/dump", 0,
2029 std::array<const char*, 1>{dumpManagerIface});
2030#endif
2031}
2032
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002033inline void requestRoutesBMCLogServiceCollection(App& app)
2034{
2035 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002036 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002037 .methods(boost::beast::http::verb::get)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002038 std::bind_front(handleLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002039}
Ed Tanous1da66f72018-07-27 16:13:37 -07002040
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002041inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002042{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002043 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002044 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002045 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002046 [&app](const crow::Request& req,
2047 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002048 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002049 {
2050 return;
2051 }
2052 asyncResp->res.jsonValue["@odata.type"] =
2053 "#LogService.v1_1_0.LogService";
2054 asyncResp->res.jsonValue["@odata.id"] =
2055 "/redfish/v1/Managers/bmc/LogServices/Journal";
2056 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2057 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2058 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2059 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302060
Ed Tanous002d39b2022-05-31 08:59:27 -07002061 std::pair<std::string, std::string> redfishDateTimeOffset =
2062 crow::utility::getDateTimeOffsetNow();
2063 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2064 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2065 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302066
Ed Tanous002d39b2022-05-31 08:59:27 -07002067 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2068 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2069 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002070}
Jason M. Billse1f26342018-07-18 12:12:00 -07002071
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002072static int
2073 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2074 sd_journal* journal,
2075 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002076{
2077 // Get the Log Entry contents
2078 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002079
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002080 std::string message;
2081 std::string_view syslogID;
2082 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2083 if (ret < 0)
2084 {
2085 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2086 << strerror(-ret);
2087 }
2088 if (!syslogID.empty())
2089 {
2090 message += std::string(syslogID) + ": ";
2091 }
2092
Ed Tanous39e77502019-03-04 17:35:53 -08002093 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002094 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002095 if (ret < 0)
2096 {
2097 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2098 return 1;
2099 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002100 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002101
2102 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002103 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002104 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002105 if (ret < 0)
2106 {
2107 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002108 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002109
2110 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002111 std::string entryTimeStr;
2112 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002113 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002114 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002115 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002116
2117 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07002118 bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
2119 bmcJournalLogEntryJson["@odata.id"] =
2120 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2121 bmcJournalLogEntryID;
2122 bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2123 bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2124 bmcJournalLogEntryJson["Message"] = std::move(message);
2125 bmcJournalLogEntryJson["EntryType"] = "Oem";
2126 bmcJournalLogEntryJson["Severity"] = severity <= 2 ? "Critical"
2127 : severity <= 4 ? "Warning"
2128 : "OK";
2129 bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2130 bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
Jason M. Billse1f26342018-07-18 12:12:00 -07002131 return 0;
2132}
2133
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002134inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002135{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002136 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002137 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002138 .methods(boost::beast::http::verb::get)(
2139 [&app](const crow::Request& req,
2140 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2141 query_param::QueryCapabilities capabilities = {
2142 .canDelegateTop = true,
2143 .canDelegateSkip = true,
2144 };
2145 query_param::Query delegatedQuery;
2146 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002147 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002148 {
2149 return;
2150 }
Ed Tanous3648c8b2022-07-25 13:39:59 -07002151
2152 size_t skip = delegatedQuery.skip.value_or(0);
2153 size_t top =
2154 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
2155
Ed Tanous002d39b2022-05-31 08:59:27 -07002156 // Collections don't include the static data added by SubRoute
2157 // because it has a duplicate entry for members
2158 asyncResp->res.jsonValue["@odata.type"] =
2159 "#LogEntryCollection.LogEntryCollection";
2160 asyncResp->res.jsonValue["@odata.id"] =
2161 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2162 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2163 asyncResp->res.jsonValue["Description"] =
2164 "Collection of BMC Journal Entries";
2165 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2166 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002167
Ed Tanous002d39b2022-05-31 08:59:27 -07002168 // Go through the journal and use the timestamp to create a
2169 // unique ID for each entry
2170 sd_journal* journalTmp = nullptr;
2171 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2172 if (ret < 0)
2173 {
2174 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2175 messages::internalError(asyncResp->res);
2176 return;
2177 }
2178 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2179 journalTmp, sd_journal_close);
2180 journalTmp = nullptr;
2181 uint64_t entryCount = 0;
2182 // Reset the unique ID on the first entry
2183 bool firstEntry = true;
2184 SD_JOURNAL_FOREACH(journal.get())
2185 {
2186 entryCount++;
2187 // Handle paging using skip (number of entries to skip from
2188 // the start) and top (number of entries to display)
Ed Tanous3648c8b2022-07-25 13:39:59 -07002189 if (entryCount <= skip || entryCount > skip + top)
George Liu0fda0f12021-11-16 10:06:17 +08002190 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002191 continue;
2192 }
2193
2194 std::string idStr;
2195 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2196 {
2197 continue;
2198 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002199 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002200
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002201 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002202 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2203 bmcJournalLogEntry) != 0)
2204 {
George Liu0fda0f12021-11-16 10:06:17 +08002205 messages::internalError(asyncResp->res);
2206 return;
2207 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002208 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002209 }
2210 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
Ed Tanous3648c8b2022-07-25 13:39:59 -07002211 if (skip + top < entryCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07002212 {
2213 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2214 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Ed Tanous3648c8b2022-07-25 13:39:59 -07002215 std::to_string(skip + top);
Ed Tanous002d39b2022-05-31 08:59:27 -07002216 }
George Liu0fda0f12021-11-16 10:06:17 +08002217 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002218}
Jason M. Billse1f26342018-07-18 12:12:00 -07002219
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002220inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002221{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002222 BMCWEB_ROUTE(app,
2223 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002224 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002225 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002226 [&app](const crow::Request& req,
2227 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2228 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002229 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002230 {
2231 return;
2232 }
2233 // Convert the unique ID back to a timestamp to find the entry
2234 uint64_t ts = 0;
2235 uint64_t index = 0;
2236 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2237 {
2238 return;
2239 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002240
Ed Tanous002d39b2022-05-31 08:59:27 -07002241 sd_journal* journalTmp = nullptr;
2242 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2243 if (ret < 0)
2244 {
2245 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2246 messages::internalError(asyncResp->res);
2247 return;
2248 }
2249 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2250 journalTmp, sd_journal_close);
2251 journalTmp = nullptr;
2252 // Go to the timestamp in the log and move to the entry at the
2253 // index tracking the unique ID
2254 std::string idStr;
2255 bool firstEntry = true;
2256 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2257 if (ret < 0)
2258 {
2259 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2260 << strerror(-ret);
2261 messages::internalError(asyncResp->res);
2262 return;
2263 }
2264 for (uint64_t i = 0; i <= index; i++)
2265 {
2266 sd_journal_next(journal.get());
2267 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2268 {
2269 messages::internalError(asyncResp->res);
2270 return;
2271 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002272 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002273 }
2274 // Confirm that the entry ID matches what was requested
2275 if (idStr != entryID)
2276 {
2277 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2278 return;
2279 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002280
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002281 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002282 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002283 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002284 {
2285 messages::internalError(asyncResp->res);
2286 return;
2287 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07002288 asyncResp->res.jsonValue.update(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002289 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002290}
2291
Claire Weinanfdd26902022-03-01 14:18:25 -08002292inline void
2293 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2294 const std::string& dumpType)
2295{
2296 std::string dumpPath;
2297 std::string overWritePolicy;
2298 bool collectDiagnosticDataSupported = false;
2299
2300 if (dumpType == "BMC")
2301 {
2302 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2303 overWritePolicy = "WrapsWhenFull";
2304 collectDiagnosticDataSupported = true;
2305 }
2306 else if (dumpType == "FaultLog")
2307 {
2308 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2309 overWritePolicy = "Unknown";
2310 collectDiagnosticDataSupported = false;
2311 }
2312 else if (dumpType == "System")
2313 {
2314 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2315 overWritePolicy = "WrapsWhenFull";
2316 collectDiagnosticDataSupported = true;
2317 }
2318 else
2319 {
2320 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2321 << dumpType;
2322 messages::internalError(asyncResp->res);
2323 return;
2324 }
2325
2326 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2327 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2328 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2329 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2330 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2331 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2332
2333 std::pair<std::string, std::string> redfishDateTimeOffset =
2334 crow::utility::getDateTimeOffsetNow();
2335 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2336 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2337 redfishDateTimeOffset.second;
2338
2339 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2340 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2341 dumpPath + "/Actions/LogService.ClearLog";
2342
2343 if (collectDiagnosticDataSupported)
2344 {
2345 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2346 ["target"] =
2347 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2348 }
2349}
2350
2351inline void handleLogServicesDumpServiceGet(
2352 crow::App& app, const std::string& dumpType, const crow::Request& req,
2353 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2354{
2355 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2356 {
2357 return;
2358 }
2359 getDumpServiceInfo(asyncResp, dumpType);
2360}
2361
2362inline void handleLogServicesDumpEntriesCollectionGet(
2363 crow::App& app, const std::string& dumpType, const crow::Request& req,
2364 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2365{
2366 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2367 {
2368 return;
2369 }
2370 getDumpEntryCollection(asyncResp, dumpType);
2371}
2372
2373inline void handleLogServicesDumpEntryGet(
2374 crow::App& app, const std::string& dumpType, const crow::Request& req,
2375 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2376 const std::string& dumpId)
2377{
2378 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2379 {
2380 return;
2381 }
2382 getDumpEntryById(asyncResp, dumpId, dumpType);
2383}
2384
2385inline void handleLogServicesDumpEntryDelete(
2386 crow::App& app, const std::string& dumpType, const crow::Request& req,
2387 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2388 const std::string& dumpId)
2389{
2390 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2391 {
2392 return;
2393 }
2394 deleteDumpEntry(asyncResp, dumpId, dumpType);
2395}
2396
2397inline void handleLogServicesDumpCollectDiagnosticDataPost(
2398 crow::App& app, const std::string& dumpType, const crow::Request& req,
2399 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2400{
2401 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2402 {
2403 return;
2404 }
2405 createDump(asyncResp, req, dumpType);
2406}
2407
2408inline void handleLogServicesDumpClearLogPost(
2409 crow::App& app, const std::string& dumpType, const crow::Request& req,
2410 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2411{
2412 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2413 {
2414 return;
2415 }
2416 clearDump(asyncResp, dumpType);
2417}
2418
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002419inline void requestRoutesBMCDumpService(App& app)
2420{
2421 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002422 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002423 .methods(boost::beast::http::verb::get)(std::bind_front(
2424 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002425}
2426
2427inline void requestRoutesBMCDumpEntryCollection(App& app)
2428{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002429 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002430 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002431 .methods(boost::beast::http::verb::get)(std::bind_front(
2432 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002433}
2434
2435inline void requestRoutesBMCDumpEntry(App& app)
2436{
2437 BMCWEB_ROUTE(app,
2438 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002439 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002440 .methods(boost::beast::http::verb::get)(std::bind_front(
2441 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2442
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002443 BMCWEB_ROUTE(app,
2444 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002445 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002446 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2447 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002448}
2449
2450inline void requestRoutesBMCDumpCreate(App& app)
2451{
George Liu0fda0f12021-11-16 10:06:17 +08002452 BMCWEB_ROUTE(
2453 app,
2454 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002455 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002456 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002457 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2458 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002459}
2460
2461inline void requestRoutesBMCDumpClear(App& app)
2462{
George Liu0fda0f12021-11-16 10:06:17 +08002463 BMCWEB_ROUTE(
2464 app,
2465 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002466 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002467 .methods(boost::beast::http::verb::post)(std::bind_front(
2468 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2469}
2470
2471inline void requestRoutesFaultLogDumpService(App& app)
2472{
2473 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2474 .privileges(redfish::privileges::getLogService)
2475 .methods(boost::beast::http::verb::get)(std::bind_front(
2476 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2477}
2478
2479inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2480{
2481 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2482 .privileges(redfish::privileges::getLogEntryCollection)
2483 .methods(boost::beast::http::verb::get)(
2484 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2485 std::ref(app), "FaultLog"));
2486}
2487
2488inline void requestRoutesFaultLogDumpEntry(App& app)
2489{
2490 BMCWEB_ROUTE(app,
2491 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2492 .privileges(redfish::privileges::getLogEntry)
2493 .methods(boost::beast::http::verb::get)(std::bind_front(
2494 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2495
2496 BMCWEB_ROUTE(app,
2497 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2498 .privileges(redfish::privileges::deleteLogEntry)
2499 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2500 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2501}
2502
2503inline void requestRoutesFaultLogDumpClear(App& app)
2504{
2505 BMCWEB_ROUTE(
2506 app,
2507 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2508 .privileges(redfish::privileges::postLogService)
2509 .methods(boost::beast::http::verb::post)(std::bind_front(
2510 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002511}
2512
2513inline void requestRoutesSystemDumpService(App& app)
2514{
2515 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002516 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002517 .methods(boost::beast::http::verb::get)(
2518 [&app](const crow::Request& req,
2519 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002520 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002521 {
2522 return;
2523 }
2524 asyncResp->res.jsonValue["@odata.id"] =
2525 "/redfish/v1/Systems/system/LogServices/Dump";
2526 asyncResp->res.jsonValue["@odata.type"] =
2527 "#LogService.v1_2_0.LogService";
2528 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2529 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2530 asyncResp->res.jsonValue["Id"] = "Dump";
2531 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302532
Ed Tanous002d39b2022-05-31 08:59:27 -07002533 std::pair<std::string, std::string> redfishDateTimeOffset =
2534 crow::utility::getDateTimeOffsetNow();
2535 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2536 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2537 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302538
Ed Tanous002d39b2022-05-31 08:59:27 -07002539 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2540 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2541 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2542 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002543
Ed Tanous002d39b2022-05-31 08:59:27 -07002544 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2545 ["target"] =
2546 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002547 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002548}
2549
2550inline void requestRoutesSystemDumpEntryCollection(App& app)
2551{
2552
2553 /**
2554 * Functions triggers appropriate requests on DBus
2555 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002556 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002557 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002558 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002559 [&app](const crow::Request& req,
2560 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002561 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002562 {
2563 return;
2564 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002565 getDumpEntryCollection(asyncResp, "System");
2566 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002567}
2568
2569inline void requestRoutesSystemDumpEntry(App& app)
2570{
2571 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002572 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002573 .privileges(redfish::privileges::getLogEntry)
2574
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002575 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002576 [&app](const crow::Request& req,
2577 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2578 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002579 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002580 {
2581 return;
2582 }
2583 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002584 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002585
2586 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002587 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002588 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002589 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002590 [&app](const crow::Request& req,
2591 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2592 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002593 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002594 {
2595 return;
2596 }
2597 deleteDumpEntry(asyncResp, param, "system");
2598 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002599}
2600
2601inline void requestRoutesSystemDumpCreate(App& app)
2602{
George Liu0fda0f12021-11-16 10:06:17 +08002603 BMCWEB_ROUTE(
2604 app,
2605 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002606 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002607 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002608 [&app](const crow::Request& req,
2609 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002610 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002611 {
2612 return;
2613 }
2614 createDump(asyncResp, req, "System");
2615 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002616}
2617
2618inline void requestRoutesSystemDumpClear(App& app)
2619{
George Liu0fda0f12021-11-16 10:06:17 +08002620 BMCWEB_ROUTE(
2621 app,
2622 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002623 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002624 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002625 [&app](const crow::Request& req,
2626 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002627
Ed Tanous45ca1b82022-03-25 13:07:27 -07002628 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002629 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002630 {
2631 return;
2632 }
2633 clearDump(asyncResp, "System");
2634 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002635}
2636
2637inline void requestRoutesCrashdumpService(App& app)
2638{
2639 // Note: Deviated from redfish privilege registry for GET & HEAD
2640 // method for security reasons.
2641 /**
2642 * Functions triggers appropriate requests on DBus
2643 */
2644 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002645 // This is incorrect, should be:
2646 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002647 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002648 .methods(boost::beast::http::verb::get)(
2649 [&app](const crow::Request& req,
2650 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002651 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002652 {
2653 return;
2654 }
2655 // Copy over the static data to include the entries added by
2656 // SubRoute
2657 asyncResp->res.jsonValue["@odata.id"] =
2658 "/redfish/v1/Systems/system/LogServices/Crashdump";
2659 asyncResp->res.jsonValue["@odata.type"] =
2660 "#LogService.v1_2_0.LogService";
2661 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2662 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2663 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2664 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2665 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302666
Ed Tanous002d39b2022-05-31 08:59:27 -07002667 std::pair<std::string, std::string> redfishDateTimeOffset =
2668 crow::utility::getDateTimeOffsetNow();
2669 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2670 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2671 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302672
Ed Tanous002d39b2022-05-31 08:59:27 -07002673 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2674 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2675 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2676 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2677 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2678 ["target"] =
2679 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002680 });
2681}
2682
2683void inline requestRoutesCrashdumpClear(App& app)
2684{
George Liu0fda0f12021-11-16 10:06:17 +08002685 BMCWEB_ROUTE(
2686 app,
2687 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002688 // This is incorrect, should be:
2689 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002690 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002691 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002692 [&app](const crow::Request& req,
2693 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002694 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002695 {
2696 return;
2697 }
2698 crow::connections::systemBus->async_method_call(
2699 [asyncResp](const boost::system::error_code ec,
2700 const std::string&) {
2701 if (ec)
2702 {
2703 messages::internalError(asyncResp->res);
2704 return;
2705 }
2706 messages::success(asyncResp->res);
2707 },
2708 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2709 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002710}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002711
zhanghch058d1b46d2021-04-01 11:18:24 +08002712static void
2713 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2714 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002715{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002716 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002717 [asyncResp, logID,
2718 &logEntryJson](const boost::system::error_code ec,
2719 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002720 if (ec)
2721 {
2722 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2723 if (ec.value() ==
2724 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002725 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002726 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002727 }
2728 else
2729 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002730 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002731 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002732 return;
2733 }
2734
2735 std::string timestamp{};
2736 std::string filename{};
2737 std::string logfile{};
2738 parseCrashdumpParameters(params, filename, timestamp, logfile);
2739
2740 if (filename.empty() || timestamp.empty())
2741 {
2742 messages::resourceMissingAtURI(asyncResp->res,
2743 crow::utility::urlFromPieces(logID));
2744 return;
2745 }
2746
2747 std::string crashdumpURI =
2748 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2749 logID + "/" + filename;
Jason M. Bills84afc482022-06-24 12:38:23 -07002750 nlohmann::json::object_t logEntry;
2751 logEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
2752 logEntry["@odata.id"] =
2753 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
2754 logEntry["Name"] = "CPU Crashdump";
2755 logEntry["Id"] = logID;
2756 logEntry["EntryType"] = "Oem";
2757 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2758 logEntry["DiagnosticDataType"] = "OEM";
2759 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2760 logEntry["Created"] = std::move(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07002761
2762 // If logEntryJson references an array of LogEntry resources
2763 // ('Members' list), then push this as a new entry, otherwise set it
2764 // directly
2765 if (logEntryJson.is_array())
2766 {
2767 logEntryJson.push_back(logEntry);
2768 asyncResp->res.jsonValue["Members@odata.count"] =
2769 logEntryJson.size();
2770 }
2771 else
2772 {
Jason M. Billsd405bb52022-06-24 10:52:05 -07002773 logEntryJson.update(logEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002774 }
2775 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002776 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002777 std::move(getStoredLogCallback), crashdumpObject,
2778 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002779 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002780}
2781
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002782inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002783{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002784 // Note: Deviated from redfish privilege registry for GET & HEAD
2785 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002786 /**
2787 * Functions triggers appropriate requests on DBus
2788 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002789 BMCWEB_ROUTE(app,
2790 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002791 // This is incorrect, should be.
2792 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002793 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002794 .methods(boost::beast::http::verb::get)(
2795 [&app](const crow::Request& req,
2796 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002797 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002798 {
2799 return;
2800 }
2801 crow::connections::systemBus->async_method_call(
2802 [asyncResp](const boost::system::error_code ec,
2803 const std::vector<std::string>& resp) {
2804 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002805 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002806 if (ec.value() !=
2807 boost::system::errc::no_such_file_or_directory)
2808 {
2809 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2810 << ec.message();
2811 messages::internalError(asyncResp->res);
2812 return;
2813 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002814 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002815 asyncResp->res.jsonValue["@odata.type"] =
2816 "#LogEntryCollection.LogEntryCollection";
2817 asyncResp->res.jsonValue["@odata.id"] =
2818 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2819 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2820 asyncResp->res.jsonValue["Description"] =
2821 "Collection of Crashdump Entries";
2822 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2823 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002824
Ed Tanous002d39b2022-05-31 08:59:27 -07002825 for (const std::string& path : resp)
2826 {
2827 const sdbusplus::message::object_path objPath(path);
2828 // Get the log ID
2829 std::string logID = objPath.filename();
2830 if (logID.empty())
2831 {
2832 continue;
2833 }
2834 // Add the log entry to the array
2835 logCrashdumpEntry(asyncResp, logID,
2836 asyncResp->res.jsonValue["Members"]);
2837 }
2838 },
2839 "xyz.openbmc_project.ObjectMapper",
2840 "/xyz/openbmc_project/object_mapper",
2841 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2842 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002843 });
2844}
Ed Tanous1da66f72018-07-27 16:13:37 -07002845
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002846inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002847{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002848 // Note: Deviated from redfish privilege registry for GET & HEAD
2849 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002850
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002851 BMCWEB_ROUTE(
2852 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002853 // this is incorrect, should be
2854 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002855 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002856 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002857 [&app](const crow::Request& req,
2858 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2859 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002860 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002861 {
2862 return;
2863 }
2864 const std::string& logID = param;
2865 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2866 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002867}
Ed Tanous1da66f72018-07-27 16:13:37 -07002868
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002869inline void requestRoutesCrashdumpFile(App& app)
2870{
2871 // Note: Deviated from redfish privilege registry for GET & HEAD
2872 // method for security reasons.
2873 BMCWEB_ROUTE(
2874 app,
2875 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002876 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002877 .methods(boost::beast::http::verb::get)(
Nan Zhoua4ce1142022-08-02 18:45:25 +00002878 [](const crow::Request& req,
2879 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2880 const std::string& logID, const std::string& fileName) {
Shounak Mitra2a9beee2022-07-20 18:41:30 +00002881 // Do not call getRedfishRoute here since the crashdump file is not a
2882 // Redfish resource.
Ed Tanous002d39b2022-05-31 08:59:27 -07002883 auto getStoredLogCallback =
2884 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2885 const boost::system::error_code ec,
2886 const std::vector<
2887 std::pair<std::string, dbus::utility::DbusVariantType>>&
2888 resp) {
2889 if (ec)
2890 {
2891 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2892 messages::internalError(asyncResp->res);
2893 return;
2894 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002895
Ed Tanous002d39b2022-05-31 08:59:27 -07002896 std::string dbusFilename{};
2897 std::string dbusTimestamp{};
2898 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002899
Ed Tanous002d39b2022-05-31 08:59:27 -07002900 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2901 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002902
Ed Tanous002d39b2022-05-31 08:59:27 -07002903 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2904 dbusFilepath.empty())
2905 {
2906 messages::resourceMissingAtURI(asyncResp->res, url);
2907 return;
2908 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002909
Ed Tanous002d39b2022-05-31 08:59:27 -07002910 // Verify the file name parameter is correct
2911 if (fileName != dbusFilename)
2912 {
2913 messages::resourceMissingAtURI(asyncResp->res, url);
2914 return;
2915 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002916
Ed Tanous002d39b2022-05-31 08:59:27 -07002917 if (!std::filesystem::exists(dbusFilepath))
2918 {
2919 messages::resourceMissingAtURI(asyncResp->res, url);
2920 return;
2921 }
2922 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2923 asyncResp->res.body() =
2924 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002925
Ed Tanous002d39b2022-05-31 08:59:27 -07002926 // Configure this to be a file download when accessed
2927 // from a browser
2928 asyncResp->res.addHeader("Content-Disposition", "attachment");
2929 };
2930 crow::connections::systemBus->async_method_call(
2931 std::move(getStoredLogCallback), crashdumpObject,
2932 crashdumpPath + std::string("/") + logID,
2933 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2934 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002935}
2936
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002937enum class OEMDiagnosticType
2938{
2939 onDemand,
2940 telemetry,
2941 invalid,
2942};
2943
Ed Tanousf7725d72022-03-07 12:46:00 -08002944inline OEMDiagnosticType
2945 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002946{
2947 if (oemDiagStr == "OnDemand")
2948 {
2949 return OEMDiagnosticType::onDemand;
2950 }
2951 if (oemDiagStr == "Telemetry")
2952 {
2953 return OEMDiagnosticType::telemetry;
2954 }
2955
2956 return OEMDiagnosticType::invalid;
2957}
2958
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002959inline void requestRoutesCrashdumpCollect(App& app)
2960{
2961 // Note: Deviated from redfish privilege registry for GET & HEAD
2962 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002963 BMCWEB_ROUTE(
2964 app,
2965 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002966 // The below is incorrect; Should be ConfigureManager
2967 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002968 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002969 .methods(boost::beast::http::verb::post)(
2970 [&app](const crow::Request& req,
2971 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002972 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002973 {
2974 return;
2975 }
2976 std::string diagnosticDataType;
2977 std::string oemDiagnosticDataType;
2978 if (!redfish::json_util::readJsonAction(
2979 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
2980 "OEMDiagnosticDataType", oemDiagnosticDataType))
2981 {
2982 return;
2983 }
2984
2985 if (diagnosticDataType != "OEM")
2986 {
2987 BMCWEB_LOG_ERROR
2988 << "Only OEM DiagnosticDataType supported for Crashdump";
2989 messages::actionParameterValueFormatError(
2990 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2991 "CollectDiagnosticData");
2992 return;
2993 }
2994
2995 OEMDiagnosticType oemDiagType =
2996 getOEMDiagnosticType(oemDiagnosticDataType);
2997
2998 std::string iface;
2999 std::string method;
3000 std::string taskMatchStr;
3001 if (oemDiagType == OEMDiagnosticType::onDemand)
3002 {
3003 iface = crashdumpOnDemandInterface;
3004 method = "GenerateOnDemandLog";
3005 taskMatchStr = "type='signal',"
3006 "interface='org.freedesktop.DBus.Properties',"
3007 "member='PropertiesChanged',"
3008 "arg0namespace='com.intel.crashdump'";
3009 }
3010 else if (oemDiagType == OEMDiagnosticType::telemetry)
3011 {
3012 iface = crashdumpTelemetryInterface;
3013 method = "GenerateTelemetryLog";
3014 taskMatchStr = "type='signal',"
3015 "interface='org.freedesktop.DBus.Properties',"
3016 "member='PropertiesChanged',"
3017 "arg0namespace='com.intel.crashdump'";
3018 }
3019 else
3020 {
3021 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3022 << oemDiagnosticDataType;
3023 messages::actionParameterValueFormatError(
3024 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3025 "CollectDiagnosticData");
3026 return;
3027 }
3028
3029 auto collectCrashdumpCallback =
3030 [asyncResp, payload(task::Payload(req)),
3031 taskMatchStr](const boost::system::error_code ec,
3032 const std::string&) mutable {
3033 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003034 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003035 if (ec.value() == boost::system::errc::operation_not_supported)
3036 {
3037 messages::resourceInStandby(asyncResp->res);
3038 }
3039 else if (ec.value() ==
3040 boost::system::errc::device_or_resource_busy)
3041 {
3042 messages::serviceTemporarilyUnavailable(asyncResp->res,
3043 "60");
3044 }
3045 else
3046 {
3047 messages::internalError(asyncResp->res);
3048 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003049 return;
3050 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003051 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Patrick Williams59d494e2022-07-22 19:26:55 -05003052 [](boost::system::error_code err, sdbusplus::message_t&,
Ed Tanous002d39b2022-05-31 08:59:27 -07003053 const std::shared_ptr<task::TaskData>& taskData) {
3054 if (!err)
3055 {
3056 taskData->messages.emplace_back(messages::taskCompletedOK(
3057 std::to_string(taskData->index)));
3058 taskData->state = "Completed";
3059 }
3060 return task::completed;
3061 },
3062 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003063
Ed Tanous002d39b2022-05-31 08:59:27 -07003064 task->startTimer(std::chrono::minutes(5));
3065 task->populateResp(asyncResp->res);
3066 task->payload.emplace(std::move(payload));
3067 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003068
Ed Tanous002d39b2022-05-31 08:59:27 -07003069 crow::connections::systemBus->async_method_call(
3070 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3071 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003072 });
3073}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003074
Andrew Geisslercb92c032018-08-17 07:56:14 -07003075/**
3076 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3077 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003078inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003079{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003080 /**
3081 * Function handles POST method request.
3082 * The Clear Log actions does not require any parameter.The action deletes
3083 * all entries found in the Entries collection for this Log Service.
3084 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003085
George Liu0fda0f12021-11-16 10:06:17 +08003086 BMCWEB_ROUTE(
3087 app,
3088 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003089 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003090 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003091 [&app](const crow::Request& req,
3092 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003093 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003094 {
3095 return;
3096 }
3097 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003098
Ed Tanous002d39b2022-05-31 08:59:27 -07003099 // Process response from Logging service.
3100 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3101 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3102 if (ec)
3103 {
3104 // TODO Handle for specific error code
3105 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3106 asyncResp->res.result(
3107 boost::beast::http::status::internal_server_error);
3108 return;
3109 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003110
Ed Tanous002d39b2022-05-31 08:59:27 -07003111 asyncResp->res.result(boost::beast::http::status::no_content);
3112 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003113
Ed Tanous002d39b2022-05-31 08:59:27 -07003114 // Make call to Logging service to request Clear Log
3115 crow::connections::systemBus->async_method_call(
3116 respHandler, "xyz.openbmc_project.Logging",
3117 "/xyz/openbmc_project/logging",
3118 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3119 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003120}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003121
3122/****************************************************
3123 * Redfish PostCode interfaces
3124 * using DBUS interface: getPostCodesTS
3125 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003126inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003127{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003128 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003129 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003130 .methods(boost::beast::http::verb::get)(
3131 [&app](const crow::Request& req,
3132 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003133 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003134 {
3135 return;
3136 }
Ed Tanous14766872022-03-15 10:44:42 -07003137
Ed Tanous002d39b2022-05-31 08:59:27 -07003138 asyncResp->res.jsonValue["@odata.id"] =
3139 "/redfish/v1/Systems/system/LogServices/PostCodes";
3140 asyncResp->res.jsonValue["@odata.type"] =
3141 "#LogService.v1_1_0.LogService";
3142 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3143 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3144 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3145 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3146 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3147 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303148
Ed Tanous002d39b2022-05-31 08:59:27 -07003149 std::pair<std::string, std::string> redfishDateTimeOffset =
3150 crow::utility::getDateTimeOffsetNow();
3151 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3152 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3153 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303154
Ed Tanous002d39b2022-05-31 08:59:27 -07003155 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3156 {"target",
3157 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003158 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003159}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003160
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003161inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003162{
George Liu0fda0f12021-11-16 10:06:17 +08003163 BMCWEB_ROUTE(
3164 app,
3165 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003166 // The following privilege is incorrect; It should be ConfigureManager
3167 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003168 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003169 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003170 [&app](const crow::Request& req,
3171 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003172 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003173 {
3174 return;
3175 }
3176 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003177
Ed Tanous002d39b2022-05-31 08:59:27 -07003178 // Make call to post-code service to request clear all
3179 crow::connections::systemBus->async_method_call(
3180 [asyncResp](const boost::system::error_code ec) {
3181 if (ec)
3182 {
3183 // TODO Handle for specific error code
3184 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3185 << ec;
3186 asyncResp->res.result(
3187 boost::beast::http::status::internal_server_error);
3188 messages::internalError(asyncResp->res);
3189 return;
3190 }
3191 },
3192 "xyz.openbmc_project.State.Boot.PostCode0",
3193 "/xyz/openbmc_project/State/Boot/PostCode0",
3194 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3195 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003196}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003197
3198static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003199 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303200 const boost::container::flat_map<
3201 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003202 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3203 const uint64_t skip = 0, const uint64_t top = 0)
3204{
3205 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003206 const registries::Message* message =
3207 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003208
3209 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003210 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003211
3212 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303213 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3214 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003215 {
3216 currentCodeIndex++;
3217 std::string postcodeEntryID =
3218 "B" + std::to_string(bootIndex) + "-" +
3219 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3220
3221 uint64_t usecSinceEpoch = code.first;
3222 uint64_t usTimeOffset = 0;
3223
3224 if (1 == currentCodeIndex)
3225 { // already incremented
3226 firstCodeTimeUs = code.first;
3227 }
3228 else
3229 {
3230 usTimeOffset = code.first - firstCodeTimeUs;
3231 }
3232
3233 // skip if no specific codeIndex is specified and currentCodeIndex does
3234 // not fall between top and skip
3235 if ((codeIndex == 0) &&
3236 (currentCodeIndex <= skip || currentCodeIndex > top))
3237 {
3238 continue;
3239 }
3240
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003241 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003242 // currentIndex
3243 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3244 {
3245 // This is done for simplicity. 1st entry is needed to calculate
3246 // time offset. To improve efficiency, one can get to the entry
3247 // directly (possibly with flatmap's nth method)
3248 continue;
3249 }
3250
3251 // currentCodeIndex is within top and skip or equal to specified code
3252 // index
3253
3254 // Get the Created time from the timestamp
3255 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003256 entryTimeStr =
3257 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003258
3259 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3260 std::ostringstream hexCode;
3261 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303262 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003263 std::ostringstream timeOffsetStr;
3264 // Set Fixed -Point Notation
3265 timeOffsetStr << std::fixed;
3266 // Set precision to 4 digits
3267 timeOffsetStr << std::setprecision(4);
3268 // Add double to stream
3269 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3270 std::vector<std::string> messageArgs = {
3271 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3272
3273 // Get MessageArgs template from message registry
3274 std::string msg;
3275 if (message != nullptr)
3276 {
3277 msg = message->message;
3278
3279 // fill in this post code value
3280 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003281 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003282 {
3283 std::string argStr = "%" + std::to_string(++i);
3284 size_t argPos = msg.find(argStr);
3285 if (argPos != std::string::npos)
3286 {
3287 msg.replace(argPos, argStr.length(), messageArg);
3288 }
3289 }
3290 }
3291
Tim Leed4342a92020-04-27 11:47:58 +08003292 // Get Severity template from message registry
3293 std::string severity;
3294 if (message != nullptr)
3295 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003296 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003297 }
3298
ZhikuiRena3316fc2020-01-29 14:58:08 -08003299 // add to AsyncResp
3300 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003301 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills84afc482022-06-24 12:38:23 -07003302 bmcLogEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
3303 bmcLogEntry["@odata.id"] =
3304 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3305 postcodeEntryID;
3306 bmcLogEntry["Name"] = "POST Code Log Entry";
3307 bmcLogEntry["Id"] = postcodeEntryID;
3308 bmcLogEntry["Message"] = std::move(msg);
3309 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3310 bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3311 bmcLogEntry["EntryType"] = "Event";
3312 bmcLogEntry["Severity"] = std::move(severity);
3313 bmcLogEntry["Created"] = entryTimeStr;
George Liu647b3cd2021-07-05 12:43:56 +08003314 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3315 {
3316 bmcLogEntry["AdditionalDataURI"] =
3317 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3318 postcodeEntryID + "/attachment";
3319 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003320 }
3321}
3322
zhanghch058d1b46d2021-04-01 11:18:24 +08003323static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003324 const uint16_t bootIndex,
3325 const uint64_t codeIndex)
3326{
3327 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303328 [aResp, bootIndex,
3329 codeIndex](const boost::system::error_code ec,
3330 const boost::container::flat_map<
3331 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3332 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003333 if (ec)
3334 {
3335 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3336 messages::internalError(aResp->res);
3337 return;
3338 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003339
Ed Tanous002d39b2022-05-31 08:59:27 -07003340 // skip the empty postcode boots
3341 if (postcode.empty())
3342 {
3343 return;
3344 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003345
Ed Tanous002d39b2022-05-31 08:59:27 -07003346 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003347
Ed Tanous002d39b2022-05-31 08:59:27 -07003348 aResp->res.jsonValue["Members@odata.count"] =
3349 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003350 },
Jonathan Doman15124762021-01-07 17:54:17 -08003351 "xyz.openbmc_project.State.Boot.PostCode0",
3352 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003353 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3354 bootIndex);
3355}
3356
zhanghch058d1b46d2021-04-01 11:18:24 +08003357static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003358 const uint16_t bootIndex,
3359 const uint16_t bootCount,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003360 const uint64_t entryCount, size_t skip,
3361 size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003362{
3363 crow::connections::systemBus->async_method_call(
3364 [aResp, bootIndex, bootCount, entryCount, skip,
3365 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303366 const boost::container::flat_map<
3367 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3368 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003369 if (ec)
3370 {
3371 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3372 messages::internalError(aResp->res);
3373 return;
3374 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003375
Ed Tanous002d39b2022-05-31 08:59:27 -07003376 uint64_t endCount = entryCount;
3377 if (!postcode.empty())
3378 {
3379 endCount = entryCount + postcode.size();
Ed Tanous3648c8b2022-07-25 13:39:59 -07003380 if (skip < endCount && (top + skip) > entryCount)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003381 {
Ed Tanous3648c8b2022-07-25 13:39:59 -07003382 uint64_t thisBootSkip =
3383 std::max(static_cast<uint64_t>(skip), entryCount) -
3384 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003385 uint64_t thisBootTop =
Ed Tanous3648c8b2022-07-25 13:39:59 -07003386 std::min(static_cast<uint64_t>(top + skip), endCount) -
3387 entryCount;
Ed Tanous002d39b2022-05-31 08:59:27 -07003388
3389 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3390 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003391 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003392 aResp->res.jsonValue["Members@odata.count"] = endCount;
3393 }
3394
3395 // continue to previous bootIndex
3396 if (bootIndex < bootCount)
3397 {
3398 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3399 bootCount, endCount, skip, top);
3400 }
Jiaqing Zhao81584ab2022-07-28 00:33:45 +08003401 else if (skip + top < endCount)
Ed Tanous002d39b2022-05-31 08:59:27 -07003402 {
3403 aResp->res.jsonValue["Members@odata.nextLink"] =
3404 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3405 std::to_string(skip + top);
3406 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003407 },
Jonathan Doman15124762021-01-07 17:54:17 -08003408 "xyz.openbmc_project.State.Boot.PostCode0",
3409 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003410 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3411 bootIndex);
3412}
3413
zhanghch058d1b46d2021-04-01 11:18:24 +08003414static void
3415 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Ed Tanous3648c8b2022-07-25 13:39:59 -07003416 size_t skip, size_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003417{
3418 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003419 sdbusplus::asio::getProperty<uint16_t>(
3420 *crow::connections::systemBus,
3421 "xyz.openbmc_project.State.Boot.PostCode0",
3422 "/xyz/openbmc_project/State/Boot/PostCode0",
3423 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3424 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3425 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003426 if (ec)
3427 {
3428 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3429 messages::internalError(aResp->res);
3430 return;
3431 }
3432 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003433 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003434}
3435
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003436inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003437{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003438 BMCWEB_ROUTE(app,
3439 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003440 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003441 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003442 [&app](const crow::Request& req,
3443 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003444 query_param::QueryCapabilities capabilities = {
3445 .canDelegateTop = true,
3446 .canDelegateSkip = true,
3447 };
3448 query_param::Query delegatedQuery;
3449 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003450 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003451 {
3452 return;
3453 }
3454 asyncResp->res.jsonValue["@odata.type"] =
3455 "#LogEntryCollection.LogEntryCollection";
3456 asyncResp->res.jsonValue["@odata.id"] =
3457 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3458 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3459 asyncResp->res.jsonValue["Description"] =
3460 "Collection of POST Code Log Entries";
3461 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3462 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Ed Tanous3648c8b2022-07-25 13:39:59 -07003463 size_t skip = delegatedQuery.skip.value_or(0);
3464 size_t top =
3465 delegatedQuery.top.value_or(query_param::maxEntriesPerPage);
3466 getCurrentBootNumber(asyncResp, skip, top);
Ed Tanous002d39b2022-05-31 08:59:27 -07003467 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003468}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003469
George Liu647b3cd2021-07-05 12:43:56 +08003470/**
3471 * @brief Parse post code ID and get the current value and index value
3472 * eg: postCodeID=B1-2, currentValue=1, index=2
3473 *
3474 * @param[in] postCodeID Post Code ID
3475 * @param[out] currentValue Current value
3476 * @param[out] index Index value
3477 *
3478 * @return bool true if the parsing is successful, false the parsing fails
3479 */
3480inline static bool parsePostCode(const std::string& postCodeID,
3481 uint64_t& currentValue, uint16_t& index)
3482{
3483 std::vector<std::string> split;
3484 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3485 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3486 {
3487 return false;
3488 }
3489
Ed Tanousca45aa32022-01-07 09:28:45 -08003490 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003491 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003492 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003493 const char* end = split[0].data() + split[0].size();
3494 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3495
3496 if (ptrIndex != end || ecIndex != std::errc())
3497 {
3498 return false;
3499 }
3500
3501 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003502
3503 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003504 end = split[1].data() + split[1].size();
3505 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003506
Tony Lee517d9a52022-06-28 15:41:23 +08003507 return ptrValue == end && ecValue == std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003508}
3509
3510inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3511{
George Liu0fda0f12021-11-16 10:06:17 +08003512 BMCWEB_ROUTE(
3513 app,
3514 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003515 .privileges(redfish::privileges::getLogEntry)
3516 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003517 [&app](const crow::Request& req,
3518 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3519 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003520 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003521 {
3522 return;
3523 }
3524 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3525 {
3526 asyncResp->res.result(boost::beast::http::status::bad_request);
3527 return;
3528 }
George Liu647b3cd2021-07-05 12:43:56 +08003529
Ed Tanous002d39b2022-05-31 08:59:27 -07003530 uint64_t currentValue = 0;
3531 uint16_t index = 0;
3532 if (!parsePostCode(postCodeID, currentValue, index))
3533 {
3534 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3535 return;
3536 }
George Liu647b3cd2021-07-05 12:43:56 +08003537
Ed Tanous002d39b2022-05-31 08:59:27 -07003538 crow::connections::systemBus->async_method_call(
3539 [asyncResp, postCodeID, currentValue](
3540 const boost::system::error_code ec,
3541 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3542 postcodes) {
3543 if (ec.value() == EBADR)
3544 {
3545 messages::resourceNotFound(asyncResp->res, "LogEntry",
3546 postCodeID);
3547 return;
3548 }
3549 if (ec)
3550 {
3551 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3552 messages::internalError(asyncResp->res);
3553 return;
3554 }
George Liu647b3cd2021-07-05 12:43:56 +08003555
Ed Tanous002d39b2022-05-31 08:59:27 -07003556 size_t value = static_cast<size_t>(currentValue) - 1;
3557 if (value == std::string::npos || postcodes.size() < currentValue)
3558 {
3559 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3560 messages::resourceNotFound(asyncResp->res, "LogEntry",
3561 postCodeID);
3562 return;
3563 }
George Liu647b3cd2021-07-05 12:43:56 +08003564
Ed Tanous002d39b2022-05-31 08:59:27 -07003565 const auto& [tID, c] = postcodes[value];
3566 if (c.empty())
3567 {
3568 BMCWEB_LOG_INFO << "No found post code data";
3569 messages::resourceNotFound(asyncResp->res, "LogEntry",
3570 postCodeID);
3571 return;
3572 }
3573 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3574 const char* d = reinterpret_cast<const char*>(c.data());
3575 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003576
Ed Tanous002d39b2022-05-31 08:59:27 -07003577 asyncResp->res.addHeader("Content-Type",
3578 "application/octet-stream");
3579 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
3580 asyncResp->res.body() = crow::utility::base64encode(strData);
3581 },
3582 "xyz.openbmc_project.State.Boot.PostCode0",
3583 "/xyz/openbmc_project/State/Boot/PostCode0",
3584 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3585 });
George Liu647b3cd2021-07-05 12:43:56 +08003586}
3587
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003588inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003589{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003590 BMCWEB_ROUTE(
3591 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003592 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003593 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003594 [&app](const crow::Request& req,
3595 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3596 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003597 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003598 {
3599 return;
3600 }
3601 uint16_t bootIndex = 0;
3602 uint64_t codeIndex = 0;
3603 if (!parsePostCode(targetID, codeIndex, bootIndex))
3604 {
3605 // Requested ID was not found
3606 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3607 return;
3608 }
3609 if (bootIndex == 0 || codeIndex == 0)
3610 {
3611 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3612 << targetID;
3613 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003614
Ed Tanous002d39b2022-05-31 08:59:27 -07003615 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3616 asyncResp->res.jsonValue["@odata.id"] =
3617 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3618 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3619 asyncResp->res.jsonValue["Description"] =
3620 "Collection of POST Code Log Entries";
3621 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3622 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003623
Ed Tanous002d39b2022-05-31 08:59:27 -07003624 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3625 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003626}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003627
Ed Tanous1da66f72018-07-27 16:13:37 -07003628} // namespace redfish