blob: 8a0bd3a1899e2720e5cb0f108326594796f1609b [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Spencer Kub7028eb2021-10-26 15:27:35 +080018#include "gzfile.hpp"
George Liu647b3cd2021-07-05 12:43:56 +080019#include "http_utility.hpp"
Spencer Kub7028eb2021-10-26 15:27:35 +080020#include "human_sort.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070021#include "registries.hpp"
22#include "registries/base_message_registry.hpp"
23#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080024#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070025
Jason M. Billse1f26342018-07-18 12:12:00 -070026#include <systemd/sd-journal.h>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060027#include <unistd.h>
Jason M. Billse1f26342018-07-18 12:12:00 -070028
John Edward Broadbent7e860f12021-04-08 15:57:16 -070029#include <app.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060030#include <boost/algorithm/string/replace.hpp>
Jason M. Bills4851d452019-03-28 11:27:48 -070031#include <boost/algorithm/string/split.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060032#include <boost/beast/http.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070033#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080034#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080035#include <dbus_utility.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070036#include <error_messages.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070037#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050038
George Liu647b3cd2021-07-05 12:43:56 +080039#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070040#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080041#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070042#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070043#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080044#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070045
46namespace redfish
47{
48
Gunnar Mills1214b7e2020-06-04 10:11:30 -050049constexpr char const* crashdumpObject = "com.intel.crashdump";
50constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050051constexpr char const* crashdumpInterface = "com.intel.crashdump";
52constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070053 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050054constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070055 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070056constexpr char const* crashdumpTelemetryInterface =
57 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070058
Ed Tanousfffb8c12022-02-07 23:53:03 -080059namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -070060{
Ed Tanous26702d02021-11-03 15:02:33 -070061static const Message*
62 getMessageFromRegistry(const std::string& messageKey,
63 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070064{
Ed Tanous26702d02021-11-03 15:02:33 -070065 std::span<const MessageEntry>::iterator messageIt = std::find_if(
66 registry.begin(), registry.end(),
67 [&messageKey](const MessageEntry& messageEntry) {
Ed Tanouse662eae2022-01-25 10:39:19 -080068 return std::strcmp(messageEntry.first, messageKey.c_str()) == 0;
Ed Tanous26702d02021-11-03 15:02:33 -070069 });
70 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070071 {
72 return &messageIt->second;
73 }
74
75 return nullptr;
76}
77
Gunnar Mills1214b7e2020-06-04 10:11:30 -050078static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070079{
80 // Redfish MessageIds are in the form
81 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
82 // the right Message
83 std::vector<std::string> fields;
84 fields.reserve(4);
85 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050086 std::string& registryName = fields[0];
87 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070088
89 // Find the right registry and check it for the MessageKey
90 if (std::string(base::header.registryPrefix) == registryName)
91 {
92 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070093 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070094 }
95 if (std::string(openbmc::header.registryPrefix) == registryName)
96 {
97 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070098 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070099 }
100 return nullptr;
101}
Ed Tanousfffb8c12022-02-07 23:53:03 -0800102} // namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -0700103
James Feistf6150402019-01-08 10:36:20 -0800104namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700105
Ed Tanous168e20c2021-12-13 14:39:53 -0800106using GetManagedPropertyType =
107 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>;
Andrew Geisslercb92c032018-08-17 07:56:14 -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
Ed Tanous67df0732021-10-26 11:23:56 -0700185static bool getSkipParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
186 const crow::Request& req, uint64_t& skip)
187{
188 boost::urls::params_view::iterator it = req.urlView.params().find("$skip");
189 if (it != req.urlView.params().end())
190 {
191 std::from_chars_result r = std::from_chars(
192 (*it).value.data(), (*it).value.data() + (*it).value.size(), skip);
193 if (r.ec != std::errc())
194 {
195 messages::queryParameterValueTypeError(asyncResp->res, "", "$skip");
196 return false;
197 }
198 }
199 return true;
200}
201
202static constexpr const uint64_t maxEntriesPerPage = 1000;
203static bool getTopParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
204 const crow::Request& req, uint64_t& top)
205{
206 boost::urls::params_view::iterator it = req.urlView.params().find("$top");
207 if (it != req.urlView.params().end())
208 {
209 std::from_chars_result r = std::from_chars(
210 (*it).value.data(), (*it).value.data() + (*it).value.size(), top);
211 if (r.ec != std::errc())
212 {
213 messages::queryParameterValueTypeError(asyncResp->res, "", "$top");
214 return false;
215 }
216 if (top < 1U || top > maxEntriesPerPage)
217 {
218
219 messages::queryParameterOutOfRange(
220 asyncResp->res, std::to_string(top), "$top",
221 "1-" + std::to_string(maxEntriesPerPage));
222 return false;
223 }
224 }
225 return true;
226}
227
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700228inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
229 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700230{
231 int ret = 0;
232 static uint64_t prevTs = 0;
233 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700234 if (firstEntry)
235 {
236 prevTs = 0;
237 }
238
Jason M. Bills16428a12018-11-02 12:42:29 -0700239 // Get the entry timestamp
240 uint64_t curTs = 0;
241 ret = sd_journal_get_realtime_usec(journal, &curTs);
242 if (ret < 0)
243 {
244 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
245 << strerror(-ret);
246 return false;
247 }
248 // If the timestamp isn't unique, increment the index
249 if (curTs == prevTs)
250 {
251 index++;
252 }
253 else
254 {
255 // Otherwise, reset it
256 index = 0;
257 }
258 // Save the timestamp
259 prevTs = curTs;
260
261 entryID = std::to_string(curTs);
262 if (index > 0)
263 {
264 entryID += "_" + std::to_string(index);
265 }
266 return true;
267}
268
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500269static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700270 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700271{
Ed Tanous271584a2019-07-09 16:24:22 -0700272 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700273 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700274 if (firstEntry)
275 {
276 prevTs = 0;
277 }
278
Jason M. Bills95820182019-04-22 16:25:34 -0700279 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700280 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700281 std::tm timeStruct = {};
282 std::istringstream entryStream(logEntry);
283 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
284 {
285 curTs = std::mktime(&timeStruct);
286 }
287 // If the timestamp isn't unique, increment the index
288 if (curTs == prevTs)
289 {
290 index++;
291 }
292 else
293 {
294 // Otherwise, reset it
295 index = 0;
296 }
297 // Save the timestamp
298 prevTs = curTs;
299
300 entryID = std::to_string(curTs);
301 if (index > 0)
302 {
303 entryID += "_" + std::to_string(index);
304 }
305 return true;
306}
307
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700308inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800309 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
310 const std::string& entryID, uint64_t& timestamp,
311 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700312{
313 if (entryID.empty())
314 {
315 return false;
316 }
317 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800318 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700319
Ed Tanous81ce6092020-12-17 16:54:55 +0000320 auto underscorePos = tsStr.find('_');
Ed Tanous71d5d8d2022-01-25 11:04:33 -0800321 if (underscorePos != std::string_view::npos)
Jason M. Bills16428a12018-11-02 12:42:29 -0700322 {
323 // Timestamp has an index
324 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800325 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700326 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700327 auto [ptr, ec] = std::from_chars(
328 indexStr.data(), indexStr.data() + indexStr.size(), index);
329 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700330 {
Ed Tanousace85d62021-10-26 12:45:59 -0700331 messages::resourceMissingAtURI(
332 asyncResp->res, crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700333 return false;
334 }
335 }
336 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700337 auto [ptr, ec] =
338 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
339 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 {
Ed Tanousace85d62021-10-26 12:45:59 -0700341 messages::resourceMissingAtURI(asyncResp->res,
342 crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700343 return false;
344 }
345 return true;
346}
347
Jason M. Bills95820182019-04-22 16:25:34 -0700348static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500349 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700350{
351 static const std::filesystem::path redfishLogDir = "/var/log";
352 static const std::string redfishLogFilename = "redfish";
353
354 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500355 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700356 std::filesystem::directory_iterator(redfishLogDir))
357 {
358 // If we find a redfish log file, save the path
359 std::string filename = dirEnt.path().filename();
360 if (boost::starts_with(filename, redfishLogFilename))
361 {
362 redfishLogFiles.emplace_back(redfishLogDir / filename);
363 }
364 }
365 // As the log files rotate, they are appended with a ".#" that is higher for
366 // the older logs. Since we don't expect more than 10 log files, we
367 // can just sort the list to get them in order from newest to oldest
368 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
369
370 return !redfishLogFiles.empty();
371}
372
zhanghch058d1b46d2021-04-01 11:18:24 +0800373inline void
374 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
375 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500376{
377 std::string dumpPath;
378 if (dumpType == "BMC")
379 {
380 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
381 }
382 else if (dumpType == "System")
383 {
384 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
385 }
386 else
387 {
388 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
389 messages::internalError(asyncResp->res);
390 return;
391 }
392
393 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800394 [asyncResp, dumpPath,
395 dumpType](const boost::system::error_code ec,
396 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500397 if (ec)
398 {
399 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
400 messages::internalError(asyncResp->res);
401 return;
402 }
403
404 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
405 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500406 std::string dumpEntryPath =
407 "/xyz/openbmc_project/dump/" +
408 std::string(boost::algorithm::to_lower_copy(dumpType)) +
409 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500410
411 for (auto& object : resp)
412 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500413 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500414 {
415 continue;
416 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800417 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500418 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500419 std::string dumpStatus;
420 nlohmann::json thisEntry;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000421
422 std::string entryID = object.first.filename();
423 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500424 {
425 continue;
426 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500427
428 for (auto& interfaceMap : object.second)
429 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500430 if (interfaceMap.first ==
431 "xyz.openbmc_project.Common.Progress")
432 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800433 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500434 {
435 if (propertyMap.first == "Status")
436 {
Ed Tanous55f79e62022-01-25 11:26:16 -0800437 const auto* status = std::get_if<std::string>(
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500438 &propertyMap.second);
439 if (status == nullptr)
440 {
441 messages::internalError(asyncResp->res);
442 break;
443 }
444 dumpStatus = *status;
445 }
446 }
447 }
448 else if (interfaceMap.first ==
449 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500450 {
451
452 for (auto& propertyMap : interfaceMap.second)
453 {
454 if (propertyMap.first == "Size")
455 {
Ed Tanous55f79e62022-01-25 11:26:16 -0800456 const auto* sizePtr =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500457 std::get_if<uint64_t>(&propertyMap.second);
458 if (sizePtr == nullptr)
459 {
460 messages::internalError(asyncResp->res);
461 break;
462 }
463 size = *sizePtr;
464 break;
465 }
466 }
467 }
468 else if (interfaceMap.first ==
469 "xyz.openbmc_project.Time.EpochTime")
470 {
471
Ed Tanous9eb808c2022-01-25 10:19:23 -0800472 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500473 {
474 if (propertyMap.first == "Elapsed")
475 {
476 const uint64_t* usecsTimeStamp =
477 std::get_if<uint64_t>(&propertyMap.second);
478 if (usecsTimeStamp == nullptr)
479 {
480 messages::internalError(asyncResp->res);
481 break;
482 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800483 timestamp = (*usecsTimeStamp / 1000 / 1000);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500484 break;
485 }
486 }
487 }
488 }
489
George Liu0fda0f12021-11-16 10:06:17 +0800490 if (dumpStatus !=
491 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500492 !dumpStatus.empty())
493 {
494 // Dump status is not Complete, no need to enumerate
495 continue;
496 }
497
George Liu647b3cd2021-07-05 12:43:56 +0800498 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500499 thisEntry["@odata.id"] = dumpPath + entryID;
500 thisEntry["Id"] = entryID;
501 thisEntry["EntryType"] = "Event";
Nan Zhou1d8782e2021-11-29 22:23:18 -0800502 thisEntry["Created"] =
503 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500504 thisEntry["Name"] = dumpType + " Dump Entry";
505
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500506 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500507
508 if (dumpType == "BMC")
509 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500510 thisEntry["DiagnosticDataType"] = "Manager";
511 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500512 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
513 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500514 }
515 else if (dumpType == "System")
516 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500517 thisEntry["DiagnosticDataType"] = "OEM";
518 thisEntry["OEMDiagnosticDataType"] = "System";
519 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500520 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
521 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500522 }
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500523 entriesArray.push_back(std::move(thisEntry));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500524 }
525 asyncResp->res.jsonValue["Members@odata.count"] =
526 entriesArray.size();
527 },
528 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
529 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
530}
531
zhanghch058d1b46d2021-04-01 11:18:24 +0800532inline void
533 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
534 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500535{
536 std::string dumpPath;
537 if (dumpType == "BMC")
538 {
539 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
540 }
541 else if (dumpType == "System")
542 {
543 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
544 }
545 else
546 {
547 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
548 messages::internalError(asyncResp->res);
549 return;
550 }
551
552 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800553 [asyncResp, entryID, dumpPath,
554 dumpType](const boost::system::error_code ec,
555 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500556 if (ec)
557 {
558 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
559 messages::internalError(asyncResp->res);
560 return;
561 }
562
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500563 bool foundDumpEntry = false;
564 std::string dumpEntryPath =
565 "/xyz/openbmc_project/dump/" +
566 std::string(boost::algorithm::to_lower_copy(dumpType)) +
567 "/entry/";
568
Ed Tanous9eb808c2022-01-25 10:19:23 -0800569 for (const auto& objectPath : resp)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500570 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500571 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500572 {
573 continue;
574 }
575
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500576 foundDumpEntry = true;
Nan Zhou1d8782e2021-11-29 22:23:18 -0800577 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500578 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500579 std::string dumpStatus;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500580
Ed Tanous9eb808c2022-01-25 10:19:23 -0800581 for (const auto& interfaceMap : objectPath.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500582 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500583 if (interfaceMap.first ==
584 "xyz.openbmc_project.Common.Progress")
585 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800586 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500587 {
588 if (propertyMap.first == "Status")
589 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800590 const std::string* status =
591 std::get_if<std::string>(
592 &propertyMap.second);
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500593 if (status == nullptr)
594 {
595 messages::internalError(asyncResp->res);
596 break;
597 }
598 dumpStatus = *status;
599 }
600 }
601 }
602 else if (interfaceMap.first ==
603 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500604 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800605 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500606 {
607 if (propertyMap.first == "Size")
608 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800609 const uint64_t* sizePtr =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500610 std::get_if<uint64_t>(&propertyMap.second);
611 if (sizePtr == nullptr)
612 {
613 messages::internalError(asyncResp->res);
614 break;
615 }
616 size = *sizePtr;
617 break;
618 }
619 }
620 }
621 else if (interfaceMap.first ==
622 "xyz.openbmc_project.Time.EpochTime")
623 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800624 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500625 {
626 if (propertyMap.first == "Elapsed")
627 {
628 const uint64_t* usecsTimeStamp =
629 std::get_if<uint64_t>(&propertyMap.second);
630 if (usecsTimeStamp == nullptr)
631 {
632 messages::internalError(asyncResp->res);
633 break;
634 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800635 timestamp = *usecsTimeStamp / 1000 / 1000;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500636 break;
637 }
638 }
639 }
640 }
641
George Liu0fda0f12021-11-16 10:06:17 +0800642 if (dumpStatus !=
643 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500644 !dumpStatus.empty())
645 {
646 // Dump status is not Complete
647 // return not found until status is changed to Completed
648 messages::resourceNotFound(asyncResp->res,
649 dumpType + " dump", entryID);
650 return;
651 }
652
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500653 asyncResp->res.jsonValue["@odata.type"] =
George Liu647b3cd2021-07-05 12:43:56 +0800654 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500655 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
656 asyncResp->res.jsonValue["Id"] = entryID;
657 asyncResp->res.jsonValue["EntryType"] = "Event";
658 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800659 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500660 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
661
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500662 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500663
664 if (dumpType == "BMC")
665 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500666 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
667 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500668 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
669 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500670 }
671 else if (dumpType == "System")
672 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500673 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
674 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500675 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500676 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500677 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
678 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500679 }
680 }
Ed Tanouse05aec52022-01-25 10:28:56 -0800681 if (!foundDumpEntry)
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500682 {
683 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
684 messages::internalError(asyncResp->res);
685 return;
686 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500687 },
688 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
689 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
690}
691
zhanghch058d1b46d2021-04-01 11:18:24 +0800692inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800693 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500694 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500695{
George Liu3de8d8b2021-03-22 17:49:39 +0800696 auto respHandler = [asyncResp,
697 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500698 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
699 if (ec)
700 {
George Liu3de8d8b2021-03-22 17:49:39 +0800701 if (ec.value() == EBADR)
702 {
703 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
704 return;
705 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500706 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
707 << ec;
708 messages::internalError(asyncResp->res);
709 return;
710 }
711 };
712 crow::connections::systemBus->async_method_call(
713 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500714 "/xyz/openbmc_project/dump/" +
715 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
716 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500717 "xyz.openbmc_project.Object.Delete", "Delete");
718}
719
zhanghch058d1b46d2021-04-01 11:18:24 +0800720inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700721 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
723 const uint32_t& dumpId, const std::string& dumpPath,
724 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500725{
726 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500727 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500728 boost::system::error_code err, sdbusplus::message::message& m,
729 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000730 if (err)
731 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500732 BMCWEB_LOG_ERROR << "Error in creating a dump";
733 taskData->state = "Cancelled";
734 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000735 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500736 std::vector<std::pair<
Ed Tanous168e20c2021-12-13 14:39:53 -0800737 std::string, std::vector<std::pair<
738 std::string, dbus::utility::DbusVariantType>>>>
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500739 interfacesList;
740
741 sdbusplus::message::object_path objPath;
742
743 m.read(objPath, interfacesList);
744
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500745 if (objPath.str ==
746 "/xyz/openbmc_project/dump/" +
747 std::string(boost::algorithm::to_lower_copy(dumpType)) +
748 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500749 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500750 nlohmann::json retMessage = messages::success();
751 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500752
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500753 std::string headerLoc =
754 "Location: " + dumpPath + std::to_string(dumpId);
755 taskData->payload->httpHeaders.emplace_back(
756 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500757
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500758 taskData->state = "Completed";
759 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500760 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500761 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500762 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800763 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500764 "member='InterfacesAdded', "
765 "path='/xyz/openbmc_project/dump'");
766
767 task->startTimer(std::chrono::minutes(3));
768 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700769 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500770}
771
zhanghch058d1b46d2021-04-01 11:18:24 +0800772inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
773 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500774{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500775
776 std::string dumpPath;
777 if (dumpType == "BMC")
778 {
779 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
780 }
781 else if (dumpType == "System")
782 {
783 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
784 }
785 else
786 {
787 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
788 messages::internalError(asyncResp->res);
789 return;
790 }
791
792 std::optional<std::string> diagnosticDataType;
793 std::optional<std::string> oemDiagnosticDataType;
794
Willy Tu15ed6782021-12-14 11:03:16 -0800795 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500796 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
797 "OEMDiagnosticDataType", oemDiagnosticDataType))
798 {
799 return;
800 }
801
802 if (dumpType == "System")
803 {
804 if (!oemDiagnosticDataType || !diagnosticDataType)
805 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800806 BMCWEB_LOG_ERROR
807 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500808 messages::actionParameterMissing(
809 asyncResp->res, "CollectDiagnosticData",
810 "DiagnosticDataType & OEMDiagnosticDataType");
811 return;
812 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700813 if ((*oemDiagnosticDataType != "System") ||
814 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500815 {
816 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700817 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500818 return;
819 }
820 }
821 else if (dumpType == "BMC")
822 {
823 if (!diagnosticDataType)
824 {
George Liu0fda0f12021-11-16 10:06:17 +0800825 BMCWEB_LOG_ERROR
826 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500827 messages::actionParameterMissing(
828 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
829 return;
830 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700831 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500832 {
833 BMCWEB_LOG_ERROR
834 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700835 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500836 return;
837 }
838 }
839
840 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700841 [asyncResp, payload(task::Payload(req)), dumpPath,
842 dumpType](const boost::system::error_code ec,
843 const uint32_t& dumpId) mutable {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500844 if (ec)
845 {
846 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
847 messages::internalError(asyncResp->res);
848 return;
849 }
850 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
851
Ed Tanous98be3e32021-09-16 15:05:36 -0700852 createDumpTaskCallback(std::move(payload), asyncResp, dumpId,
853 dumpPath, dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500854 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500855 "xyz.openbmc_project.Dump.Manager",
856 "/xyz/openbmc_project/dump/" +
857 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500858 "xyz.openbmc_project.Dump.Create", "CreateDump");
859}
860
zhanghch058d1b46d2021-04-01 11:18:24 +0800861inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
862 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500863{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500864 std::string dumpTypeLowerCopy =
865 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800866
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500867 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500868 [asyncResp, dumpType](const boost::system::error_code ec,
869 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500870 if (ec)
871 {
872 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
873 messages::internalError(asyncResp->res);
874 return;
875 }
876
877 for (const std::string& path : subTreePaths)
878 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000879 sdbusplus::message::object_path objPath(path);
880 std::string logID = objPath.filename();
881 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500882 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000883 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500884 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000885 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500886 }
887 },
888 "xyz.openbmc_project.ObjectMapper",
889 "/xyz/openbmc_project/object_mapper",
890 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500891 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
892 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
893 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500894}
895
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700896inline static void parseCrashdumpParameters(
Ed Tanous168e20c2021-12-13 14:39:53 -0800897 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
898 params,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500899 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700900{
901 for (auto property : params)
902 {
903 if (property.first == "Timestamp")
904 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500905 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500906 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700907 if (value != nullptr)
908 {
909 timestamp = *value;
910 }
911 }
912 else if (property.first == "Filename")
913 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500914 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500915 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700916 if (value != nullptr)
917 {
918 filename = *value;
919 }
920 }
921 else if (property.first == "Log")
922 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500923 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500924 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700925 if (value != nullptr)
926 {
927 logfile = *value;
928 }
929 }
930 }
931}
932
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500933constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700934inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700935{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800936 /**
937 * Functions triggers appropriate requests on DBus
938 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700939 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700940 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700941 .methods(boost::beast::http::verb::get)(
942 [](const crow::Request&,
943 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
944
945 {
946 // Collections don't include the static data added by SubRoute
947 // because it has a duplicate entry for members
948 asyncResp->res.jsonValue["@odata.type"] =
949 "#LogServiceCollection.LogServiceCollection";
950 asyncResp->res.jsonValue["@odata.id"] =
951 "/redfish/v1/Systems/system/LogServices";
952 asyncResp->res.jsonValue["Name"] =
953 "System Log Services Collection";
954 asyncResp->res.jsonValue["Description"] =
955 "Collection of LogServices for this Computer System";
956 nlohmann::json& logServiceArray =
957 asyncResp->res.jsonValue["Members"];
958 logServiceArray = nlohmann::json::array();
959 logServiceArray.push_back(
960 {{"@odata.id",
961 "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500962#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700963 logServiceArray.push_back(
964 {{"@odata.id",
965 "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600966#endif
967
Jason M. Billsd53dd412019-02-12 17:16:22 -0800968#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700969 logServiceArray.push_back(
970 {{"@odata.id",
971 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800972#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800973
974#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
975 logServiceArray.push_back(
976 {{"@odata.id",
977 "/redfish/v1/Systems/system/LogServices/HostLogger"}});
978#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700979 asyncResp->res.jsonValue["Members@odata.count"] =
980 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800981
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700982 crow::connections::systemBus->async_method_call(
983 [asyncResp](const boost::system::error_code ec,
984 const std::vector<std::string>& subtreePath) {
985 if (ec)
986 {
987 BMCWEB_LOG_ERROR << ec;
988 return;
989 }
ZhikuiRena3316fc2020-01-29 14:58:08 -0800990
Ed Tanous55f79e62022-01-25 11:26:16 -0800991 for (const auto& pathStr : subtreePath)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700992 {
993 if (pathStr.find("PostCode") != std::string::npos)
994 {
995 nlohmann::json& logServiceArrayLocal =
996 asyncResp->res.jsonValue["Members"];
997 logServiceArrayLocal.push_back(
George Liu0fda0f12021-11-16 10:06:17 +0800998 {{"@odata.id",
999 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001000 asyncResp->res
1001 .jsonValue["Members@odata.count"] =
1002 logServiceArrayLocal.size();
1003 return;
1004 }
1005 }
1006 },
1007 "xyz.openbmc_project.ObjectMapper",
1008 "/xyz/openbmc_project/object_mapper",
1009 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
1010 0, std::array<const char*, 1>{postCodeIface});
1011 });
1012}
1013
1014inline void requestRoutesEventLogService(App& app)
1015{
1016 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -07001017 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001018 .methods(
1019 boost::beast::http::verb::
1020 get)([](const crow::Request&,
1021 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1022 asyncResp->res.jsonValue["@odata.id"] =
1023 "/redfish/v1/Systems/system/LogServices/EventLog";
1024 asyncResp->res.jsonValue["@odata.type"] =
1025 "#LogService.v1_1_0.LogService";
1026 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1027 asyncResp->res.jsonValue["Description"] =
1028 "System Event Log Service";
1029 asyncResp->res.jsonValue["Id"] = "EventLog";
1030 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301031
1032 std::pair<std::string, std::string> redfishDateTimeOffset =
1033 crow::utility::getDateTimeOffsetNow();
1034
1035 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1036 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1037 redfishDateTimeOffset.second;
1038
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001039 asyncResp->res.jsonValue["Entries"] = {
1040 {"@odata.id",
1041 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1042 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1043
George Liu0fda0f12021-11-16 10:06:17 +08001044 {"target",
1045 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001046 });
1047}
1048
1049inline void requestRoutesJournalEventLogClear(App& app)
1050{
Jason M. Bills4978b632022-02-22 14:17:43 -08001051 BMCWEB_ROUTE(
1052 app,
1053 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001054 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001055 .methods(boost::beast::http::verb::post)(
1056 [](const crow::Request&,
1057 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1058 // Clear the EventLog by deleting the log files
1059 std::vector<std::filesystem::path> redfishLogFiles;
1060 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001061 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001062 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001063 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001064 std::error_code ec;
1065 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001066 }
1067 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001068
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001069 // Reload rsyslog so it knows to start new log files
1070 crow::connections::systemBus->async_method_call(
1071 [asyncResp](const boost::system::error_code ec) {
1072 if (ec)
1073 {
1074 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1075 << ec;
1076 messages::internalError(asyncResp->res);
1077 return;
1078 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001079
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001080 messages::success(asyncResp->res);
1081 },
1082 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1083 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1084 "rsyslog.service", "replace");
1085 });
1086}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001087
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001088static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001089 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001090 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001091{
Jason M. Bills95820182019-04-22 16:25:34 -07001092 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001093 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001094 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001095 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001096 {
1097 return 1;
1098 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001099 std::string timestamp = logEntry.substr(0, space);
1100 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001101 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001102 if (entryStart == std::string::npos)
1103 {
1104 return 1;
1105 }
1106 std::string_view entry(logEntry);
1107 entry.remove_prefix(entryStart);
1108 // Use split to separate the entry into its fields
1109 std::vector<std::string> logEntryFields;
1110 boost::split(logEntryFields, entry, boost::is_any_of(","),
1111 boost::token_compress_on);
1112 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001113 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001114 {
1115 return 1;
1116 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001117 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001118
Jason M. Bills4851d452019-03-28 11:27:48 -07001119 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001120 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001121
Jason M. Bills4851d452019-03-28 11:27:48 -07001122 std::string msg;
1123 std::string severity;
1124 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001125 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001126 msg = message->message;
Ed Tanous5f2b84e2022-02-08 00:41:53 -08001127 severity = message->messageSeverity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001128 }
1129
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001130 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001131 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001132 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001133 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001134 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001135 // If the first string is empty, assume there are no MessageArgs
1136 std::size_t messageArgsSize = 0;
1137 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001138 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001139 messageArgsSize = logEntryFields.size() - 1;
1140 }
1141
Ed Tanous23a21a12020-07-25 04:45:05 +00001142 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001143
1144 // Fill the MessageArgs into the Message
1145 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001146 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001147 {
1148 std::string argStr = "%" + std::to_string(++i);
1149 size_t argPos = msg.find(argStr);
1150 if (argPos != std::string::npos)
1151 {
1152 msg.replace(argPos, argStr.length(), messageArg);
1153 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001154 }
1155 }
1156
Jason M. Bills95820182019-04-22 16:25:34 -07001157 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1158 // format which matches the Redfish format except for the fractional seconds
1159 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001160 std::size_t dot = timestamp.find_first_of('.');
1161 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001162 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001163 {
Jason M. Bills95820182019-04-22 16:25:34 -07001164 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001165 }
1166
1167 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001168 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001169 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001170 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001171 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001172 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001173 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001174 {"Id", logEntryID},
1175 {"Message", std::move(msg)},
1176 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001177 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001178 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001179 {"Severity", std::move(severity)},
1180 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001181 return 0;
1182}
1183
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001184inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001185{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001186 BMCWEB_ROUTE(app,
1187 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001188 .privileges(redfish::privileges::getLogEntryCollection)
Jason M. Bills4978b632022-02-22 14:17:43 -08001189 .methods(
1190 boost::beast::http::verb::
1191 get)([](const crow::Request& req,
1192 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1193 uint64_t skip = 0;
1194 uint64_t top = maxEntriesPerPage; // Show max entries by default
1195 if (!getSkipParam(asyncResp, req, skip))
1196 {
1197 return;
1198 }
1199 if (!getTopParam(asyncResp, req, top))
1200 {
1201 return;
1202 }
1203 // Collections don't include the static data added by SubRoute
1204 // because it has a duplicate entry for members
1205 asyncResp->res.jsonValue["@odata.type"] =
1206 "#LogEntryCollection.LogEntryCollection";
1207 asyncResp->res.jsonValue["@odata.id"] =
1208 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1209 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1210 asyncResp->res.jsonValue["Description"] =
1211 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001212
Jason M. Bills4978b632022-02-22 14:17:43 -08001213 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1214 logEntryArray = nlohmann::json::array();
1215 // Go through the log files and create a unique ID for each
1216 // entry
1217 std::vector<std::filesystem::path> redfishLogFiles;
1218 getRedfishLogFiles(redfishLogFiles);
1219 uint64_t entryCount = 0;
1220 std::string logEntry;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001221
Jason M. Bills4978b632022-02-22 14:17:43 -08001222 // Oldest logs are in the last file, so start there and loop
1223 // backwards
1224 for (auto it = redfishLogFiles.rbegin();
1225 it < redfishLogFiles.rend(); it++)
1226 {
1227 std::ifstream logStream(*it);
1228 if (!logStream.is_open())
Andrew Geisslercb92c032018-08-17 07:56:14 -07001229 {
Jason M. Bills4978b632022-02-22 14:17:43 -08001230 continue;
1231 }
1232
1233 // Reset the unique ID on the first entry
1234 bool firstEntry = true;
1235 while (std::getline(logStream, logEntry))
1236 {
1237 entryCount++;
1238 // Handle paging using skip (number of entries to skip
1239 // from the start) and top (number of entries to
1240 // display)
1241 if (entryCount <= skip || entryCount > skip + top)
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001242 {
1243 continue;
1244 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001245
Jason M. Bills4978b632022-02-22 14:17:43 -08001246 std::string idStr;
1247 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001248 {
Jason M. Bills4978b632022-02-22 14:17:43 -08001249 continue;
1250 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001251
Jason M. Bills4978b632022-02-22 14:17:43 -08001252 if (firstEntry)
1253 {
1254 firstEntry = false;
1255 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001256
Jason M. Bills4978b632022-02-22 14:17:43 -08001257 logEntryArray.push_back({});
1258 nlohmann::json& bmcLogEntry = logEntryArray.back();
1259 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) !=
1260 0)
1261 {
1262 messages::internalError(asyncResp->res);
1263 return;
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001264 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001265 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001266 }
1267 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1268 if (skip + top < entryCount)
1269 {
1270 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1271 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
1272 std::to_string(skip + top);
1273 }
1274 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001275}
Chicago Duan336e96c2019-07-15 14:22:08 +08001276
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001277inline void requestRoutesJournalEventLogEntry(App& app)
1278{
1279 BMCWEB_ROUTE(
1280 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001281 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001282 .methods(boost::beast::http::verb::get)(
1283 [](const crow::Request&,
1284 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1285 const std::string& param) {
1286 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001287
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001288 // Go through the log files and check the unique ID for each
1289 // entry to find the target entry
1290 std::vector<std::filesystem::path> redfishLogFiles;
1291 getRedfishLogFiles(redfishLogFiles);
1292 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001293
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001294 // Oldest logs are in the last file, so start there and loop
1295 // backwards
1296 for (auto it = redfishLogFiles.rbegin();
1297 it < redfishLogFiles.rend(); it++)
1298 {
1299 std::ifstream logStream(*it);
1300 if (!logStream.is_open())
1301 {
1302 continue;
1303 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001304
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001305 // Reset the unique ID on the first entry
1306 bool firstEntry = true;
1307 while (std::getline(logStream, logEntry))
1308 {
1309 std::string idStr;
1310 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1311 {
1312 continue;
1313 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001314
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001315 if (firstEntry)
1316 {
1317 firstEntry = false;
1318 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001319
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001320 if (idStr == targetID)
1321 {
1322 if (fillEventLogEntryJson(
1323 idStr, logEntry,
1324 asyncResp->res.jsonValue) != 0)
1325 {
1326 messages::internalError(asyncResp->res);
1327 return;
1328 }
1329 return;
1330 }
1331 }
1332 }
1333 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07001334 messages::resourceMissingAtURI(
1335 asyncResp->res, crow::utility::urlFromPieces(targetID));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001336 });
1337}
1338
1339inline void requestRoutesDBusEventLogEntryCollection(App& app)
1340{
1341 BMCWEB_ROUTE(app,
1342 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001343 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001344 .methods(
1345 boost::beast::http::verb::
1346 get)([](const crow::Request&,
1347 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1348 // Collections don't include the static data added by SubRoute
1349 // because it has a duplicate entry for members
1350 asyncResp->res.jsonValue["@odata.type"] =
1351 "#LogEntryCollection.LogEntryCollection";
1352 asyncResp->res.jsonValue["@odata.id"] =
1353 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1354 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1355 asyncResp->res.jsonValue["Description"] =
1356 "Collection of System Event Log Entries";
1357
1358 // DBus implementation of EventLog/Entries
1359 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001360 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001361 [asyncResp](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001362 const dbus::utility::ManagedObjectType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001363 if (ec)
1364 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001365 // TODO Handle for specific error code
1366 BMCWEB_LOG_ERROR
1367 << "getLogEntriesIfaceData resp_handler got error "
1368 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001369 messages::internalError(asyncResp->res);
1370 return;
1371 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001372 nlohmann::json& entriesArray =
1373 asyncResp->res.jsonValue["Members"];
1374 entriesArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -08001375 for (const auto& objectPath : resp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001376 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001377 const uint32_t* id = nullptr;
Ed Tanousc419c752022-01-26 12:19:54 -08001378 const uint64_t* timestamp = nullptr;
1379 const uint64_t* updateTimestamp = nullptr;
Ed Tanous914e2d52022-01-07 11:38:34 -08001380 const std::string* severity = nullptr;
1381 const std::string* message = nullptr;
1382 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001383 bool resolved = false;
Ed Tanous9eb808c2022-01-25 10:19:23 -08001384 for (const auto& interfaceMap : objectPath.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001385 {
1386 if (interfaceMap.first ==
1387 "xyz.openbmc_project.Logging.Entry")
1388 {
Ed Tanous9eb808c2022-01-25 10:19:23 -08001389 for (const auto& propertyMap :
1390 interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001391 {
1392 if (propertyMap.first == "Id")
1393 {
1394 id = std::get_if<uint32_t>(
1395 &propertyMap.second);
1396 }
1397 else if (propertyMap.first == "Timestamp")
1398 {
Ed Tanousc419c752022-01-26 12:19:54 -08001399 timestamp = std::get_if<uint64_t>(
1400 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001401 }
1402 else if (propertyMap.first ==
1403 "UpdateTimestamp")
1404 {
Ed Tanousc419c752022-01-26 12:19:54 -08001405 updateTimestamp = std::get_if<uint64_t>(
1406 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001407 }
1408 else if (propertyMap.first == "Severity")
1409 {
1410 severity = std::get_if<std::string>(
1411 &propertyMap.second);
1412 }
1413 else if (propertyMap.first == "Message")
1414 {
1415 message = std::get_if<std::string>(
1416 &propertyMap.second);
1417 }
1418 else if (propertyMap.first == "Resolved")
1419 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001420 const bool* resolveptr =
1421 std::get_if<bool>(
1422 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001423 if (resolveptr == nullptr)
1424 {
1425 messages::internalError(
1426 asyncResp->res);
1427 return;
1428 }
1429 resolved = *resolveptr;
1430 }
1431 }
1432 if (id == nullptr || message == nullptr ||
1433 severity == nullptr)
1434 {
1435 messages::internalError(asyncResp->res);
1436 return;
1437 }
1438 }
1439 else if (interfaceMap.first ==
1440 "xyz.openbmc_project.Common.FilePath")
1441 {
Ed Tanous9eb808c2022-01-25 10:19:23 -08001442 for (const auto& propertyMap :
1443 interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001444 {
1445 if (propertyMap.first == "Path")
1446 {
1447 filePath = std::get_if<std::string>(
1448 &propertyMap.second);
1449 }
1450 }
1451 }
1452 }
1453 // Object path without the
1454 // xyz.openbmc_project.Logging.Entry interface, ignore
1455 // and continue.
1456 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001457 severity == nullptr || timestamp == nullptr ||
1458 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001459 {
1460 continue;
1461 }
1462 entriesArray.push_back({});
1463 nlohmann::json& thisEntry = entriesArray.back();
1464 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1465 thisEntry["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001466 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001467 std::to_string(*id);
1468 thisEntry["Name"] = "System Event Log Entry";
1469 thisEntry["Id"] = std::to_string(*id);
1470 thisEntry["Message"] = *message;
1471 thisEntry["Resolved"] = resolved;
1472 thisEntry["EntryType"] = "Event";
1473 thisEntry["Severity"] =
1474 translateSeverityDbusToRedfish(*severity);
1475 thisEntry["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001476 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001477 thisEntry["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001478 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001479 if (filePath != nullptr)
1480 {
1481 thisEntry["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001482 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001483 std::to_string(*id) + "/attachment";
1484 }
1485 }
1486 std::sort(entriesArray.begin(), entriesArray.end(),
1487 [](const nlohmann::json& left,
1488 const nlohmann::json& right) {
1489 return (left["Id"] <= right["Id"]);
1490 });
1491 asyncResp->res.jsonValue["Members@odata.count"] =
1492 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001493 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001494 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1495 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1496 });
1497}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001498
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001499inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001500{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001501 BMCWEB_ROUTE(
1502 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001503 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001504 .methods(boost::beast::http::verb::get)(
1505 [](const crow::Request&,
1506 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1507 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001508
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001509 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001510 std::string entryID = param;
1511 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001512
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001513 // DBus implementation of EventLog/Entries
1514 // Make call to Logging Service to find all log entry objects
1515 crow::connections::systemBus->async_method_call(
1516 [asyncResp, entryID](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001517 const GetManagedPropertyType& resp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001518 if (ec.value() == EBADR)
1519 {
1520 messages::resourceNotFound(
1521 asyncResp->res, "EventLogEntry", entryID);
1522 return;
1523 }
1524 if (ec)
1525 {
George Liu0fda0f12021-11-16 10:06:17 +08001526 BMCWEB_LOG_ERROR
1527 << "EventLogEntry (DBus) resp_handler got error "
1528 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001529 messages::internalError(asyncResp->res);
1530 return;
1531 }
Ed Tanous914e2d52022-01-07 11:38:34 -08001532 const uint32_t* id = nullptr;
Ed Tanousc419c752022-01-26 12:19:54 -08001533 const uint64_t* timestamp = nullptr;
1534 const uint64_t* updateTimestamp = nullptr;
Ed Tanous914e2d52022-01-07 11:38:34 -08001535 const std::string* severity = nullptr;
1536 const std::string* message = nullptr;
1537 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001538 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001539
Ed Tanous9eb808c2022-01-25 10:19:23 -08001540 for (const auto& propertyMap : resp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001541 {
1542 if (propertyMap.first == "Id")
1543 {
1544 id = std::get_if<uint32_t>(&propertyMap.second);
1545 }
1546 else if (propertyMap.first == "Timestamp")
1547 {
Ed Tanousc419c752022-01-26 12:19:54 -08001548 timestamp =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001549 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001550 }
1551 else if (propertyMap.first == "UpdateTimestamp")
1552 {
Ed Tanousc419c752022-01-26 12:19:54 -08001553 updateTimestamp =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001554 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001555 }
1556 else if (propertyMap.first == "Severity")
1557 {
1558 severity = std::get_if<std::string>(
1559 &propertyMap.second);
1560 }
1561 else if (propertyMap.first == "Message")
1562 {
1563 message = std::get_if<std::string>(
1564 &propertyMap.second);
1565 }
1566 else if (propertyMap.first == "Resolved")
1567 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001568 const bool* resolveptr =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001569 std::get_if<bool>(&propertyMap.second);
1570 if (resolveptr == nullptr)
1571 {
1572 messages::internalError(asyncResp->res);
1573 return;
1574 }
1575 resolved = *resolveptr;
1576 }
1577 else if (propertyMap.first == "Path")
1578 {
1579 filePath = std::get_if<std::string>(
1580 &propertyMap.second);
1581 }
1582 }
1583 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001584 severity == nullptr || timestamp == nullptr ||
1585 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001586 {
1587 messages::internalError(asyncResp->res);
1588 return;
1589 }
1590 asyncResp->res.jsonValue["@odata.type"] =
1591 "#LogEntry.v1_8_0.LogEntry";
1592 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001593 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001594 std::to_string(*id);
1595 asyncResp->res.jsonValue["Name"] =
1596 "System Event Log Entry";
1597 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1598 asyncResp->res.jsonValue["Message"] = *message;
1599 asyncResp->res.jsonValue["Resolved"] = resolved;
1600 asyncResp->res.jsonValue["EntryType"] = "Event";
1601 asyncResp->res.jsonValue["Severity"] =
1602 translateSeverityDbusToRedfish(*severity);
1603 asyncResp->res.jsonValue["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001604 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001605 asyncResp->res.jsonValue["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001606 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001607 if (filePath != nullptr)
1608 {
1609 asyncResp->res.jsonValue["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001610 "/redfish/v1/Systems/system/LogServices/EventLog/attachment/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001611 std::to_string(*id);
1612 }
1613 },
1614 "xyz.openbmc_project.Logging",
1615 "/xyz/openbmc_project/logging/entry/" + entryID,
1616 "org.freedesktop.DBus.Properties", "GetAll", "");
1617 });
1618
1619 BMCWEB_ROUTE(
1620 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001621 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001622 .methods(boost::beast::http::verb::patch)(
1623 [](const crow::Request& req,
1624 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1625 const std::string& entryId) {
1626 std::optional<bool> resolved;
1627
Willy Tu15ed6782021-12-14 11:03:16 -08001628 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1629 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001630 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001631 return;
1632 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001633 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001634
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001635 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001636 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001637 if (ec)
1638 {
1639 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1640 messages::internalError(asyncResp->res);
1641 return;
1642 }
1643 },
1644 "xyz.openbmc_project.Logging",
1645 "/xyz/openbmc_project/logging/entry/" + entryId,
1646 "org.freedesktop.DBus.Properties", "Set",
1647 "xyz.openbmc_project.Logging.Entry", "Resolved",
Ed Tanous168e20c2021-12-13 14:39:53 -08001648 dbus::utility::DbusVariantType(*resolved));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001649 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001650
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001651 BMCWEB_ROUTE(
1652 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001653 .privileges(redfish::privileges::deleteLogEntry)
1654
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001655 .methods(boost::beast::http::verb::delete_)(
1656 [](const crow::Request&,
1657 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1658 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001659
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660 {
1661 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001662
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001663 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001664
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001665 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001666
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001667 // Process response from Logging service.
1668 auto respHandler = [asyncResp, entryID](
1669 const boost::system::error_code ec) {
1670 BMCWEB_LOG_DEBUG
1671 << "EventLogEntry (DBus) doDelete callback: Done";
1672 if (ec)
1673 {
1674 if (ec.value() == EBADR)
1675 {
1676 messages::resourceNotFound(asyncResp->res,
1677 "LogEntry", entryID);
1678 return;
1679 }
1680 // TODO Handle for specific error code
George Liu0fda0f12021-11-16 10:06:17 +08001681 BMCWEB_LOG_ERROR
1682 << "EventLogEntry (DBus) doDelete respHandler got error "
1683 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001684 asyncResp->res.result(
1685 boost::beast::http::status::internal_server_error);
1686 return;
1687 }
1688
1689 asyncResp->res.result(boost::beast::http::status::ok);
1690 };
1691
1692 // Make call to Logging service to request Delete Log
1693 crow::connections::systemBus->async_method_call(
1694 respHandler, "xyz.openbmc_project.Logging",
1695 "/xyz/openbmc_project/logging/entry/" + entryID,
1696 "xyz.openbmc_project.Object.Delete", "Delete");
1697 });
1698}
1699
1700inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001701{
George Liu0fda0f12021-11-16 10:06:17 +08001702 BMCWEB_ROUTE(
1703 app,
1704 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001705 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001706 .methods(boost::beast::http::verb::get)(
1707 [](const crow::Request& req,
1708 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1709 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001710
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001711 {
George Liu647b3cd2021-07-05 12:43:56 +08001712 if (!http_helpers::isOctetAccepted(
1713 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001714 {
1715 asyncResp->res.result(
1716 boost::beast::http::status::bad_request);
1717 return;
1718 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001719
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001720 std::string entryID = param;
1721 dbus::utility::escapePathForDbus(entryID);
1722
1723 crow::connections::systemBus->async_method_call(
1724 [asyncResp,
1725 entryID](const boost::system::error_code ec,
1726 const sdbusplus::message::unix_fd& unixfd) {
1727 if (ec.value() == EBADR)
1728 {
1729 messages::resourceNotFound(
1730 asyncResp->res, "EventLogAttachment", entryID);
1731 return;
1732 }
1733 if (ec)
1734 {
1735 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1736 messages::internalError(asyncResp->res);
1737 return;
1738 }
1739
1740 int fd = -1;
1741 fd = dup(unixfd);
1742 if (fd == -1)
1743 {
1744 messages::internalError(asyncResp->res);
1745 return;
1746 }
1747
1748 long long int size = lseek(fd, 0, SEEK_END);
1749 if (size == -1)
1750 {
1751 messages::internalError(asyncResp->res);
1752 return;
1753 }
1754
1755 // Arbitrary max size of 64kb
1756 constexpr int maxFileSize = 65536;
1757 if (size > maxFileSize)
1758 {
1759 BMCWEB_LOG_ERROR
1760 << "File size exceeds maximum allowed size of "
1761 << maxFileSize;
1762 messages::internalError(asyncResp->res);
1763 return;
1764 }
1765 std::vector<char> data(static_cast<size_t>(size));
1766 long long int rc = lseek(fd, 0, SEEK_SET);
1767 if (rc == -1)
1768 {
1769 messages::internalError(asyncResp->res);
1770 return;
1771 }
1772 rc = read(fd, data.data(), data.size());
1773 if ((rc == -1) || (rc != size))
1774 {
1775 messages::internalError(asyncResp->res);
1776 return;
1777 }
1778 close(fd);
1779
1780 std::string_view strData(data.data(), data.size());
1781 std::string output =
1782 crow::utility::base64encode(strData);
1783
1784 asyncResp->res.addHeader("Content-Type",
1785 "application/octet-stream");
1786 asyncResp->res.addHeader("Content-Transfer-Encoding",
1787 "Base64");
1788 asyncResp->res.body() = std::move(output);
1789 },
1790 "xyz.openbmc_project.Logging",
1791 "/xyz/openbmc_project/logging/entry/" + entryID,
1792 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1793 });
1794}
1795
Spencer Kub7028eb2021-10-26 15:27:35 +08001796constexpr const char* hostLoggerFolderPath = "/var/log/console";
1797
1798inline bool
1799 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1800 std::vector<std::filesystem::path>& hostLoggerFiles)
1801{
1802 std::error_code ec;
1803 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1804 if (ec)
1805 {
1806 BMCWEB_LOG_ERROR << ec.message();
1807 return false;
1808 }
1809 for (const std::filesystem::directory_entry& it : logPath)
1810 {
1811 std::string filename = it.path().filename();
1812 // Prefix of each log files is "log". Find the file and save the
1813 // path
1814 if (boost::starts_with(filename, "log"))
1815 {
1816 hostLoggerFiles.emplace_back(it.path());
1817 }
1818 }
1819 // As the log files rotate, they are appended with a ".#" that is higher for
1820 // the older logs. Since we start from oldest logs, sort the name in
1821 // descending order.
1822 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1823 AlphanumLess<std::string>());
1824
1825 return true;
1826}
1827
1828inline bool
1829 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
1830 uint64_t& skip, uint64_t& top,
1831 std::vector<std::string>& logEntries, size_t& logCount)
1832{
1833 GzFileReader logFile;
1834
1835 // Go though all log files and expose host logs.
1836 for (const std::filesystem::path& it : hostLoggerFiles)
1837 {
1838 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1839 {
1840 BMCWEB_LOG_ERROR << "fail to expose host logs";
1841 return false;
1842 }
1843 }
1844 // Get lastMessage from constructor by getter
1845 std::string lastMessage = logFile.getLastMessage();
1846 if (!lastMessage.empty())
1847 {
1848 logCount++;
1849 if (logCount > skip && logCount <= (skip + top))
1850 {
1851 logEntries.push_back(lastMessage);
1852 }
1853 }
1854 return true;
1855}
1856
1857inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1858 const std::string& msg,
1859 nlohmann::json& logEntryJson)
1860{
1861 // Fill in the log entry with the gathered data.
1862 logEntryJson = {
1863 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1864 {"@odata.id",
1865 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1866 logEntryID},
1867 {"Name", "Host Logger Entry"},
1868 {"Id", logEntryID},
1869 {"Message", msg},
1870 {"EntryType", "Oem"},
1871 {"Severity", "OK"},
1872 {"OemRecordFormat", "Host Logger Entry"}};
1873}
1874
1875inline void requestRoutesSystemHostLogger(App& app)
1876{
1877 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1878 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08001879 .methods(
1880 boost::beast::http::verb::
1881 get)([](const crow::Request&,
1882 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1883 asyncResp->res.jsonValue["@odata.id"] =
1884 "/redfish/v1/Systems/system/LogServices/HostLogger";
1885 asyncResp->res.jsonValue["@odata.type"] =
1886 "#LogService.v1_1_0.LogService";
1887 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1888 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1889 asyncResp->res.jsonValue["Id"] = "HostLogger";
1890 asyncResp->res.jsonValue["Entries"] = {
1891 {"@odata.id",
1892 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries"}};
1893 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001894}
1895
1896inline void requestRoutesSystemHostLoggerCollection(App& app)
1897{
1898 BMCWEB_ROUTE(app,
1899 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1900 .privileges(redfish::privileges::getLogEntry)
George Liu0fda0f12021-11-16 10:06:17 +08001901 .methods(
1902 boost::beast::http::verb::
1903 get)([](const crow::Request& req,
1904 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1905 uint64_t skip = 0;
1906 uint64_t top = maxEntriesPerPage; // Show max 1000 entries by
1907 // default, allow range 1 to
1908 // 1000 entries per page.
1909 if (!getSkipParam(asyncResp, req, skip))
1910 {
1911 return;
1912 }
1913 if (!getTopParam(asyncResp, req, top))
1914 {
1915 return;
1916 }
1917 asyncResp->res.jsonValue["@odata.id"] =
1918 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1919 asyncResp->res.jsonValue["@odata.type"] =
1920 "#LogEntryCollection.LogEntryCollection";
1921 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1922 asyncResp->res.jsonValue["Description"] =
1923 "Collection of HostLogger Entries";
1924 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1925 logEntryArray = nlohmann::json::array();
1926 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001927
George Liu0fda0f12021-11-16 10:06:17 +08001928 std::vector<std::filesystem::path> hostLoggerFiles;
1929 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1930 {
1931 BMCWEB_LOG_ERROR << "fail to get host log file path";
1932 return;
1933 }
1934
1935 size_t logCount = 0;
1936 // This vector only store the entries we want to expose that
1937 // control by skip and top.
1938 std::vector<std::string> logEntries;
1939 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1940 logCount))
1941 {
1942 messages::internalError(asyncResp->res);
1943 return;
1944 }
1945 // If vector is empty, that means skip value larger than total
1946 // log count
Ed Tanous26f69762022-01-25 09:49:11 -08001947 if (logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001948 {
1949 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1950 return;
1951 }
Ed Tanous26f69762022-01-25 09:49:11 -08001952 if (!logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001953 {
1954 for (size_t i = 0; i < logEntries.size(); i++)
Spencer Kub7028eb2021-10-26 15:27:35 +08001955 {
George Liu0fda0f12021-11-16 10:06:17 +08001956 logEntryArray.push_back({});
1957 nlohmann::json& hostLogEntry = logEntryArray.back();
1958 fillHostLoggerEntryJson(std::to_string(skip + i),
1959 logEntries[i], hostLogEntry);
Spencer Kub7028eb2021-10-26 15:27:35 +08001960 }
1961
George Liu0fda0f12021-11-16 10:06:17 +08001962 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1963 if (skip + top < logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001964 {
George Liu0fda0f12021-11-16 10:06:17 +08001965 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1966 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
1967 std::to_string(skip + top);
Spencer Kub7028eb2021-10-26 15:27:35 +08001968 }
George Liu0fda0f12021-11-16 10:06:17 +08001969 }
1970 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001971}
1972
1973inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1974{
1975 BMCWEB_ROUTE(
1976 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1977 .privileges(redfish::privileges::getLogEntry)
1978 .methods(boost::beast::http::verb::get)(
Ed Tanousace85d62021-10-26 12:45:59 -07001979 [](const crow::Request& req,
Spencer Kub7028eb2021-10-26 15:27:35 +08001980 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1981 const std::string& param) {
1982 const std::string& targetID = param;
1983
1984 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001985
1986 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1987 const char* end = targetID.data() + targetID.size();
1988
1989 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
Spencer Kub7028eb2021-10-26 15:27:35 +08001990 if (ec == std::errc::invalid_argument)
1991 {
Ed Tanousace85d62021-10-26 12:45:59 -07001992 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
Spencer Kub7028eb2021-10-26 15:27:35 +08001993 return;
1994 }
1995 if (ec == std::errc::result_out_of_range)
1996 {
Ed Tanousace85d62021-10-26 12:45:59 -07001997 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
Spencer Kub7028eb2021-10-26 15:27:35 +08001998 return;
1999 }
2000
2001 std::vector<std::filesystem::path> hostLoggerFiles;
2002 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2003 {
2004 BMCWEB_LOG_ERROR << "fail to get host log file path";
2005 return;
2006 }
2007
2008 size_t logCount = 0;
2009 uint64_t top = 1;
2010 std::vector<std::string> logEntries;
2011 // We can get specific entry by skip and top. For example, if we
2012 // want to get nth entry, we can set skip = n-1 and top = 1 to
2013 // get that entry
2014 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
2015 logEntries, logCount))
2016 {
2017 messages::internalError(asyncResp->res);
2018 return;
2019 }
2020
2021 if (!logEntries.empty())
2022 {
2023 fillHostLoggerEntryJson(targetID, logEntries[0],
2024 asyncResp->res.jsonValue);
2025 return;
2026 }
2027
2028 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07002029 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
Spencer Kub7028eb2021-10-26 15:27:35 +08002030 });
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)(
2038 [](const crow::Request&,
2039 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2040 // Collections don't include the static data added by SubRoute
2041 // because it has a duplicate entry for members
2042 asyncResp->res.jsonValue["@odata.type"] =
2043 "#LogServiceCollection.LogServiceCollection";
2044 asyncResp->res.jsonValue["@odata.id"] =
2045 "/redfish/v1/Managers/bmc/LogServices";
2046 asyncResp->res.jsonValue["Name"] =
2047 "Open BMC Log Services Collection";
2048 asyncResp->res.jsonValue["Description"] =
2049 "Collection of LogServices for this Manager";
2050 nlohmann::json& logServiceArray =
2051 asyncResp->res.jsonValue["Members"];
2052 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002053#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002054 logServiceArray.push_back(
2055 {{"@odata.id",
2056 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002057#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002058#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002059 logServiceArray.push_back(
2060 {{"@odata.id",
2061 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002062#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002063 asyncResp->res.jsonValue["Members@odata.count"] =
2064 logServiceArray.size();
2065 });
2066}
Ed Tanous1da66f72018-07-27 16:13:37 -07002067
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002068inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002069{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002070 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002071 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002072 .methods(boost::beast::http::verb::get)(
2073 [](const crow::Request&,
2074 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002075
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002076 {
2077 asyncResp->res.jsonValue["@odata.type"] =
2078 "#LogService.v1_1_0.LogService";
2079 asyncResp->res.jsonValue["@odata.id"] =
2080 "/redfish/v1/Managers/bmc/LogServices/Journal";
2081 asyncResp->res.jsonValue["Name"] =
2082 "Open BMC Journal Log Service";
2083 asyncResp->res.jsonValue["Description"] =
2084 "BMC Journal Log Service";
2085 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2086 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302087
2088 std::pair<std::string, std::string> redfishDateTimeOffset =
2089 crow::utility::getDateTimeOffsetNow();
2090 asyncResp->res.jsonValue["DateTime"] =
2091 redfishDateTimeOffset.first;
2092 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2093 redfishDateTimeOffset.second;
2094
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002095 asyncResp->res.jsonValue["Entries"] = {
2096 {"@odata.id",
2097 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
2098 });
2099}
Jason M. Billse1f26342018-07-18 12:12:00 -07002100
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002101static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2102 sd_journal* journal,
2103 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002104{
2105 // Get the Log Entry contents
2106 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002107
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002108 std::string message;
2109 std::string_view syslogID;
2110 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2111 if (ret < 0)
2112 {
2113 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2114 << strerror(-ret);
2115 }
2116 if (!syslogID.empty())
2117 {
2118 message += std::string(syslogID) + ": ";
2119 }
2120
Ed Tanous39e77502019-03-04 17:35:53 -08002121 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002122 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002123 if (ret < 0)
2124 {
2125 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2126 return 1;
2127 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002128 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002129
2130 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002131 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002132 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002133 if (ret < 0)
2134 {
2135 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002136 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002137
2138 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002139 std::string entryTimeStr;
2140 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002141 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002142 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002143 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002144
2145 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002146 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002147 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002148 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2149 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002150 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002151 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002152 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002153 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002154 {"Severity", severity <= 2 ? "Critical"
2155 : severity <= 4 ? "Warning"
2156 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002157 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002158 {"Created", std::move(entryTimeStr)}};
2159 return 0;
2160}
2161
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002162inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002163{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002164 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002165 .privileges(redfish::privileges::getLogEntryCollection)
George Liu0fda0f12021-11-16 10:06:17 +08002166 .methods(
2167 boost::beast::http::verb::
2168 get)([](const crow::Request& req,
2169 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2170 static constexpr const long maxEntriesPerPage = 1000;
2171 uint64_t skip = 0;
2172 uint64_t top = maxEntriesPerPage; // Show max entries by default
2173 if (!getSkipParam(asyncResp, req, skip))
2174 {
2175 return;
2176 }
2177 if (!getTopParam(asyncResp, req, top))
2178 {
2179 return;
2180 }
2181 // Collections don't include the static data added by SubRoute
2182 // because it has a duplicate entry for members
2183 asyncResp->res.jsonValue["@odata.type"] =
2184 "#LogEntryCollection.LogEntryCollection";
2185 asyncResp->res.jsonValue["@odata.id"] =
2186 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2187 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2188 asyncResp->res.jsonValue["Description"] =
2189 "Collection of BMC Journal Entries";
2190 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2191 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002192
George Liu0fda0f12021-11-16 10:06:17 +08002193 // Go through the journal and use the timestamp to create a
2194 // unique ID for each entry
2195 sd_journal* journalTmp = nullptr;
2196 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2197 if (ret < 0)
2198 {
2199 BMCWEB_LOG_ERROR << "failed to open journal: "
2200 << strerror(-ret);
2201 messages::internalError(asyncResp->res);
2202 return;
2203 }
2204 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2205 journalTmp, sd_journal_close);
2206 journalTmp = nullptr;
2207 uint64_t entryCount = 0;
2208 // Reset the unique ID on the first entry
2209 bool firstEntry = true;
2210 SD_JOURNAL_FOREACH(journal.get())
2211 {
2212 entryCount++;
2213 // Handle paging using skip (number of entries to skip from
2214 // the start) and top (number of entries to display)
2215 if (entryCount <= skip || entryCount > skip + top)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002216 {
George Liu0fda0f12021-11-16 10:06:17 +08002217 continue;
2218 }
2219
2220 std::string idStr;
2221 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2222 {
2223 continue;
2224 }
2225
2226 if (firstEntry)
2227 {
2228 firstEntry = false;
2229 }
2230
2231 logEntryArray.push_back({});
2232 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2233 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2234 bmcJournalLogEntry) != 0)
2235 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002236 messages::internalError(asyncResp->res);
2237 return;
2238 }
George Liu0fda0f12021-11-16 10:06:17 +08002239 }
2240 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2241 if (skip + top < entryCount)
2242 {
2243 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2244 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2245 std::to_string(skip + top);
2246 }
2247 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002248}
Jason M. Billse1f26342018-07-18 12:12:00 -07002249
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002250inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002251{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002252 BMCWEB_ROUTE(app,
2253 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002254 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002255 .methods(boost::beast::http::verb::get)(
Ed Tanousace85d62021-10-26 12:45:59 -07002256 [](const crow::Request& req,
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002257 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2258 const std::string& entryID) {
2259 // Convert the unique ID back to a timestamp to find the entry
2260 uint64_t ts = 0;
2261 uint64_t index = 0;
2262 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2263 {
2264 return;
2265 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002266
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002267 sd_journal* journalTmp = nullptr;
2268 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2269 if (ret < 0)
2270 {
2271 BMCWEB_LOG_ERROR << "failed to open journal: "
2272 << strerror(-ret);
2273 messages::internalError(asyncResp->res);
2274 return;
2275 }
2276 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2277 journal(journalTmp, sd_journal_close);
2278 journalTmp = nullptr;
2279 // Go to the timestamp in the log and move to the entry at the
2280 // index tracking the unique ID
2281 std::string idStr;
2282 bool firstEntry = true;
2283 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2284 if (ret < 0)
2285 {
2286 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2287 << strerror(-ret);
2288 messages::internalError(asyncResp->res);
2289 return;
2290 }
2291 for (uint64_t i = 0; i <= index; i++)
2292 {
2293 sd_journal_next(journal.get());
2294 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2295 {
2296 messages::internalError(asyncResp->res);
2297 return;
2298 }
2299 if (firstEntry)
2300 {
2301 firstEntry = false;
2302 }
2303 }
2304 // Confirm that the entry ID matches what was requested
2305 if (idStr != entryID)
2306 {
Ed Tanousace85d62021-10-26 12:45:59 -07002307 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002308 return;
2309 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002310
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002311 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2312 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002313 {
2314 messages::internalError(asyncResp->res);
2315 return;
2316 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002317 });
2318}
2319
2320inline void requestRoutesBMCDumpService(App& app)
2321{
2322 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002323 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08002324 .methods(
2325 boost::beast::http::verb::
2326 get)([](const crow::Request&,
2327 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2328 asyncResp->res.jsonValue["@odata.id"] =
2329 "/redfish/v1/Managers/bmc/LogServices/Dump";
2330 asyncResp->res.jsonValue["@odata.type"] =
2331 "#LogService.v1_2_0.LogService";
2332 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2333 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2334 asyncResp->res.jsonValue["Id"] = "Dump";
2335 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302336
George Liu0fda0f12021-11-16 10:06:17 +08002337 std::pair<std::string, std::string> redfishDateTimeOffset =
2338 crow::utility::getDateTimeOffsetNow();
2339 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2340 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2341 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302342
George Liu0fda0f12021-11-16 10:06:17 +08002343 asyncResp->res.jsonValue["Entries"] = {
2344 {"@odata.id",
2345 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2346 asyncResp->res.jsonValue["Actions"] = {
2347 {"#LogService.ClearLog",
2348 {{"target",
2349 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog"}}},
2350 {"#LogService.CollectDiagnosticData",
2351 {{"target",
2352 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
2353 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002354}
2355
2356inline void requestRoutesBMCDumpEntryCollection(App& app)
2357{
2358
2359 /**
2360 * Functions triggers appropriate requests on DBus
2361 */
2362 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002363 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002364 .methods(boost::beast::http::verb::get)(
2365 [](const crow::Request&,
2366 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2367 asyncResp->res.jsonValue["@odata.type"] =
2368 "#LogEntryCollection.LogEntryCollection";
2369 asyncResp->res.jsonValue["@odata.id"] =
2370 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2371 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2372 asyncResp->res.jsonValue["Description"] =
2373 "Collection of BMC Dump Entries";
2374
2375 getDumpEntryCollection(asyncResp, "BMC");
2376 });
2377}
2378
2379inline void requestRoutesBMCDumpEntry(App& app)
2380{
2381 BMCWEB_ROUTE(app,
2382 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002383 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002384 .methods(boost::beast::http::verb::get)(
2385 [](const crow::Request&,
2386 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2387 const std::string& param) {
2388 getDumpEntryById(asyncResp, param, "BMC");
2389 });
2390 BMCWEB_ROUTE(app,
2391 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002392 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002393 .methods(boost::beast::http::verb::delete_)(
2394 [](const crow::Request&,
2395 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2396 const std::string& param) {
2397 deleteDumpEntry(asyncResp, param, "bmc");
2398 });
2399}
2400
2401inline void requestRoutesBMCDumpCreate(App& app)
2402{
2403
George Liu0fda0f12021-11-16 10:06:17 +08002404 BMCWEB_ROUTE(
2405 app,
2406 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002407 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002408 .methods(boost::beast::http::verb::post)(
2409 [](const crow::Request& req,
2410 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2411 createDump(asyncResp, req, "BMC");
2412 });
2413}
2414
2415inline void requestRoutesBMCDumpClear(App& app)
2416{
George Liu0fda0f12021-11-16 10:06:17 +08002417 BMCWEB_ROUTE(
2418 app,
2419 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002420 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002421 .methods(boost::beast::http::verb::post)(
2422 [](const crow::Request&,
2423 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2424 clearDump(asyncResp, "BMC");
2425 });
2426}
2427
2428inline void requestRoutesSystemDumpService(App& app)
2429{
2430 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002431 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002432 .methods(boost::beast::http::verb::get)(
2433 [](const crow::Request&,
2434 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2435
2436 {
2437 asyncResp->res.jsonValue["@odata.id"] =
2438 "/redfish/v1/Systems/system/LogServices/Dump";
2439 asyncResp->res.jsonValue["@odata.type"] =
2440 "#LogService.v1_2_0.LogService";
2441 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2442 asyncResp->res.jsonValue["Description"] =
2443 "System Dump LogService";
2444 asyncResp->res.jsonValue["Id"] = "Dump";
2445 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302446
2447 std::pair<std::string, std::string> redfishDateTimeOffset =
2448 crow::utility::getDateTimeOffsetNow();
2449 asyncResp->res.jsonValue["DateTime"] =
2450 redfishDateTimeOffset.first;
2451 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2452 redfishDateTimeOffset.second;
2453
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002454 asyncResp->res.jsonValue["Entries"] = {
2455 {"@odata.id",
2456 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2457 asyncResp->res.jsonValue["Actions"] = {
2458 {"#LogService.ClearLog",
2459 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002460 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002461 {"#LogService.CollectDiagnosticData",
2462 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002463 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002464 });
2465}
2466
2467inline void requestRoutesSystemDumpEntryCollection(App& app)
2468{
2469
2470 /**
2471 * Functions triggers appropriate requests on DBus
2472 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002473 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002474 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002475 .methods(boost::beast::http::verb::get)(
2476 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002477 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002478 asyncResp->res.jsonValue["@odata.type"] =
2479 "#LogEntryCollection.LogEntryCollection";
2480 asyncResp->res.jsonValue["@odata.id"] =
2481 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2482 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2483 asyncResp->res.jsonValue["Description"] =
2484 "Collection of System Dump Entries";
2485
2486 getDumpEntryCollection(asyncResp, "System");
2487 });
2488}
2489
2490inline void requestRoutesSystemDumpEntry(App& app)
2491{
2492 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002493 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002494 .privileges(redfish::privileges::getLogEntry)
2495
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002496 .methods(boost::beast::http::verb::get)(
2497 [](const crow::Request&,
2498 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2499 const std::string& param) {
2500 getDumpEntryById(asyncResp, param, "System");
2501 });
2502
2503 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002504 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002505 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002506 .methods(boost::beast::http::verb::delete_)(
2507 [](const crow::Request&,
2508 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2509 const std::string& param) {
2510 deleteDumpEntry(asyncResp, param, "system");
2511 });
2512}
2513
2514inline void requestRoutesSystemDumpCreate(App& app)
2515{
George Liu0fda0f12021-11-16 10:06:17 +08002516 BMCWEB_ROUTE(
2517 app,
2518 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002519 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002520 .methods(boost::beast::http::verb::post)(
2521 [](const crow::Request& req,
2522 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2523
2524 { createDump(asyncResp, req, "System"); });
2525}
2526
2527inline void requestRoutesSystemDumpClear(App& app)
2528{
George Liu0fda0f12021-11-16 10:06:17 +08002529 BMCWEB_ROUTE(
2530 app,
2531 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002532 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002533 .methods(boost::beast::http::verb::post)(
2534 [](const crow::Request&,
2535 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2536
2537 { clearDump(asyncResp, "System"); });
2538}
2539
2540inline void requestRoutesCrashdumpService(App& app)
2541{
2542 // Note: Deviated from redfish privilege registry for GET & HEAD
2543 // method for security reasons.
2544 /**
2545 * Functions triggers appropriate requests on DBus
2546 */
2547 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002548 // This is incorrect, should be:
2549 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002550 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002551 .methods(
2552 boost::beast::http::verb::
2553 get)([](const crow::Request&,
2554 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2555 // Copy over the static data to include the entries added by
2556 // SubRoute
2557 asyncResp->res.jsonValue["@odata.id"] =
2558 "/redfish/v1/Systems/system/LogServices/Crashdump";
2559 asyncResp->res.jsonValue["@odata.type"] =
2560 "#LogService.v1_2_0.LogService";
2561 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2562 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2563 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2564 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2565 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302566
2567 std::pair<std::string, std::string> redfishDateTimeOffset =
2568 crow::utility::getDateTimeOffsetNow();
2569 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2570 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2571 redfishDateTimeOffset.second;
2572
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002573 asyncResp->res.jsonValue["Entries"] = {
2574 {"@odata.id",
2575 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2576 asyncResp->res.jsonValue["Actions"] = {
2577 {"#LogService.ClearLog",
George Liu0fda0f12021-11-16 10:06:17 +08002578 {{"target",
2579 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002580 {"#LogService.CollectDiagnosticData",
George Liu0fda0f12021-11-16 10:06:17 +08002581 {{"target",
2582 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002583 });
2584}
2585
2586void inline requestRoutesCrashdumpClear(App& app)
2587{
George Liu0fda0f12021-11-16 10:06:17 +08002588 BMCWEB_ROUTE(
2589 app,
2590 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002591 // This is incorrect, should be:
2592 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002593 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002594 .methods(boost::beast::http::verb::post)(
2595 [](const crow::Request&,
2596 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2597 crow::connections::systemBus->async_method_call(
2598 [asyncResp](const boost::system::error_code ec,
2599 const std::string&) {
2600 if (ec)
2601 {
2602 messages::internalError(asyncResp->res);
2603 return;
2604 }
2605 messages::success(asyncResp->res);
2606 },
2607 crashdumpObject, crashdumpPath, deleteAllInterface,
2608 "DeleteAll");
2609 });
2610}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002611
zhanghch058d1b46d2021-04-01 11:18:24 +08002612static void
2613 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2614 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002615{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002616 auto getStoredLogCallback =
2617 [asyncResp, logID, &logEntryJson](
2618 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002619 const std::vector<
2620 std::pair<std::string, dbus::utility::DbusVariantType>>&
2621 params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002622 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002623 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002624 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2625 if (ec.value() ==
2626 boost::system::linux_error::bad_request_descriptor)
2627 {
2628 messages::resourceNotFound(asyncResp->res, "LogEntry",
2629 logID);
2630 }
2631 else
2632 {
2633 messages::internalError(asyncResp->res);
2634 }
2635 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002636 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002637
Johnathan Mantey043a0532020-03-10 17:15:28 -07002638 std::string timestamp{};
2639 std::string filename{};
2640 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002641 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002642
2643 if (filename.empty() || timestamp.empty())
2644 {
Ed Tanousace85d62021-10-26 12:45:59 -07002645 messages::resourceMissingAtURI(
2646 asyncResp->res, crow::utility::urlFromPieces(logID));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002647 return;
2648 }
2649
2650 std::string crashdumpURI =
2651 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2652 logID + "/" + filename;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002653 nlohmann::json logEntry = {
Jason M. Bills4978b632022-02-22 14:17:43 -08002654 {"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
2655 {"@odata.id",
2656 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2657 logID},
2658 {"Name", "CPU Crashdump"},
2659 {"Id", logID},
2660 {"EntryType", "Oem"},
2661 {"AdditionalDataURI", std::move(crashdumpURI)},
2662 {"DiagnosticDataType", "OEM"},
2663 {"OEMDiagnosticDataType", "PECICrashdump"},
2664 {"Created", std::move(timestamp)}};
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002665
2666 // If logEntryJson references an array of LogEntry resources
2667 // ('Members' list), then push this as a new entry, otherwise set it
2668 // directly
2669 if (logEntryJson.is_array())
2670 {
2671 logEntryJson.push_back(logEntry);
2672 asyncResp->res.jsonValue["Members@odata.count"] =
2673 logEntryJson.size();
2674 }
2675 else
2676 {
2677 logEntryJson = logEntry;
2678 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002679 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002680 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002681 std::move(getStoredLogCallback), crashdumpObject,
2682 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002683 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002684}
2685
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002686inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002687{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002688 // Note: Deviated from redfish privilege registry for GET & HEAD
2689 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002690 /**
2691 * Functions triggers appropriate requests on DBus
2692 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002693 BMCWEB_ROUTE(app,
2694 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002695 // This is incorrect, should be.
2696 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002697 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002698 .methods(
2699 boost::beast::http::verb::
2700 get)([](const crow::Request&,
2701 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002702 crow::connections::systemBus->async_method_call(
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002703 [asyncResp](const boost::system::error_code ec,
2704 const std::vector<std::string>& resp) {
2705 if (ec)
2706 {
2707 if (ec.value() !=
2708 boost::system::errc::no_such_file_or_directory)
2709 {
2710 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2711 << ec.message();
2712 messages::internalError(asyncResp->res);
2713 return;
2714 }
2715 }
2716 asyncResp->res.jsonValue["@odata.type"] =
2717 "#LogEntryCollection.LogEntryCollection";
2718 asyncResp->res.jsonValue["@odata.id"] =
2719 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2720 asyncResp->res.jsonValue["Name"] =
2721 "Open BMC Crashdump Entries";
2722 asyncResp->res.jsonValue["Description"] =
2723 "Collection of Crashdump Entries";
2724 asyncResp->res.jsonValue["Members"] =
2725 nlohmann::json::array();
2726
2727 for (const std::string& path : resp)
2728 {
2729 const sdbusplus::message::object_path objPath(path);
2730 // Get the log ID
2731 std::string logID = objPath.filename();
2732 if (logID.empty())
2733 {
2734 continue;
2735 }
2736 // Add the log entry to the array
2737 logCrashdumpEntry(asyncResp, logID,
2738 asyncResp->res.jsonValue["Members"]);
2739 }
2740 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002741 "xyz.openbmc_project.ObjectMapper",
2742 "/xyz/openbmc_project/object_mapper",
2743 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2744 std::array<const char*, 1>{crashdumpInterface});
2745 });
2746}
Ed Tanous1da66f72018-07-27 16:13:37 -07002747
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002748inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002749{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002750 // Note: Deviated from redfish privilege registry for GET & HEAD
2751 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002752
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002753 BMCWEB_ROUTE(
2754 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002755 // this is incorrect, should be
2756 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002757 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002758 .methods(boost::beast::http::verb::get)(
2759 [](const crow::Request&,
2760 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2761 const std::string& param) {
2762 const std::string& logID = param;
2763 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2764 });
2765}
Ed Tanous1da66f72018-07-27 16:13:37 -07002766
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002767inline void requestRoutesCrashdumpFile(App& app)
2768{
2769 // Note: Deviated from redfish privilege registry for GET & HEAD
2770 // method for security reasons.
2771 BMCWEB_ROUTE(
2772 app,
2773 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002774 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002775 .methods(boost::beast::http::verb::get)(
Ed Tanousace85d62021-10-26 12:45:59 -07002776 [](const crow::Request& req,
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002777 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2778 const std::string& logID, const std::string& fileName) {
2779 auto getStoredLogCallback =
Ed Tanousace85d62021-10-26 12:45:59 -07002780 [asyncResp, logID, fileName,
2781 url(boost::urls::url(req.urlView))](
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002782 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002783 const std::vector<std::pair<
2784 std::string, dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002785 resp) {
2786 if (ec)
2787 {
2788 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2789 << ec.message();
2790 messages::internalError(asyncResp->res);
2791 return;
2792 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002793
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002794 std::string dbusFilename{};
2795 std::string dbusTimestamp{};
2796 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002797
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002798 parseCrashdumpParameters(resp, dbusFilename,
2799 dbusTimestamp, dbusFilepath);
2800
2801 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2802 dbusFilepath.empty())
2803 {
Ed Tanousace85d62021-10-26 12:45:59 -07002804 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002805 return;
2806 }
2807
2808 // Verify the file name parameter is correct
2809 if (fileName != dbusFilename)
2810 {
Ed Tanousace85d62021-10-26 12:45:59 -07002811 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002812 return;
2813 }
2814
2815 if (!std::filesystem::exists(dbusFilepath))
2816 {
Ed Tanousace85d62021-10-26 12:45:59 -07002817 messages::resourceMissingAtURI(asyncResp->res, url);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002818 return;
2819 }
Jason M. Bills2d314912022-01-12 13:59:01 -08002820 std::ifstream ifs(dbusFilepath,
2821 std::ios::in | std::ios::binary);
2822 asyncResp->res.body() = std::string(
2823 std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002824
2825 // Configure this to be a file download when accessed
2826 // from a browser
2827 asyncResp->res.addHeader("Content-Disposition",
2828 "attachment");
2829 };
2830 crow::connections::systemBus->async_method_call(
2831 std::move(getStoredLogCallback), crashdumpObject,
2832 crashdumpPath + std::string("/") + logID,
2833 "org.freedesktop.DBus.Properties", "GetAll",
2834 crashdumpInterface);
2835 });
2836}
2837
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002838enum class OEMDiagnosticType
2839{
2840 onDemand,
2841 telemetry,
2842 invalid,
2843};
2844
2845OEMDiagnosticType getOEMDiagnosticType(const std::string_view& oemDiagStr)
2846{
2847 if (oemDiagStr == "OnDemand")
2848 {
2849 return OEMDiagnosticType::onDemand;
2850 }
2851 if (oemDiagStr == "Telemetry")
2852 {
2853 return OEMDiagnosticType::telemetry;
2854 }
2855
2856 return OEMDiagnosticType::invalid;
2857}
2858
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002859inline void requestRoutesCrashdumpCollect(App& app)
2860{
2861 // Note: Deviated from redfish privilege registry for GET & HEAD
2862 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002863 BMCWEB_ROUTE(
2864 app,
2865 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002866 // The below is incorrect; Should be ConfigureManager
2867 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002868 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002869 .methods(
2870 boost::beast::http::verb::
2871 post)([](const crow::Request& req,
2872 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2873 std::string diagnosticDataType;
2874 std::string oemDiagnosticDataType;
Willy Tu15ed6782021-12-14 11:03:16 -08002875 if (!redfish::json_util::readJsonAction(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002876 req, asyncResp->res, "DiagnosticDataType",
2877 diagnosticDataType, "OEMDiagnosticDataType",
2878 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002879 {
James Feist46229572020-02-19 15:11:58 -08002880 return;
2881 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002882
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002883 if (diagnosticDataType != "OEM")
2884 {
2885 BMCWEB_LOG_ERROR
2886 << "Only OEM DiagnosticDataType supported for Crashdump";
2887 messages::actionParameterValueFormatError(
2888 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2889 "CollectDiagnosticData");
2890 return;
2891 }
2892
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002893 OEMDiagnosticType oemDiagType =
2894 getOEMDiagnosticType(oemDiagnosticDataType);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002895
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002896 std::string iface;
2897 std::string method;
2898 std::string taskMatchStr;
2899 if (oemDiagType == OEMDiagnosticType::onDemand)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002900 {
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002901 iface = crashdumpOnDemandInterface;
2902 method = "GenerateOnDemandLog";
2903 taskMatchStr = "type='signal',"
2904 "interface='org.freedesktop.DBus.Properties',"
2905 "member='PropertiesChanged',"
2906 "arg0namespace='com.intel.crashdump'";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002907 }
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002908 else if (oemDiagType == OEMDiagnosticType::telemetry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002909 {
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002910 iface = crashdumpTelemetryInterface;
2911 method = "GenerateTelemetryLog";
2912 taskMatchStr = "type='signal',"
2913 "interface='org.freedesktop.DBus.Properties',"
2914 "member='PropertiesChanged',"
2915 "arg0namespace='com.intel.crashdump'";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002916 }
2917 else
2918 {
2919 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2920 << oemDiagnosticDataType;
2921 messages::actionParameterValueFormatError(
2922 asyncResp->res, oemDiagnosticDataType,
2923 "OEMDiagnosticDataType", "CollectDiagnosticData");
2924 return;
2925 }
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002926
2927 auto collectCrashdumpCallback =
2928 [asyncResp, payload(task::Payload(req)),
2929 taskMatchStr](const boost::system::error_code ec,
2930 const std::string&) mutable {
2931 if (ec)
2932 {
2933 if (ec.value() ==
2934 boost::system::errc::operation_not_supported)
2935 {
2936 messages::resourceInStandby(asyncResp->res);
2937 }
2938 else if (ec.value() ==
2939 boost::system::errc::device_or_resource_busy)
2940 {
2941 messages::serviceTemporarilyUnavailable(
2942 asyncResp->res, "60");
2943 }
2944 else
2945 {
2946 messages::internalError(asyncResp->res);
2947 }
2948 return;
2949 }
2950 std::shared_ptr<task::TaskData> task =
2951 task::TaskData::createTask(
2952 [](boost::system::error_code err,
2953 sdbusplus::message::message&,
2954 const std::shared_ptr<task::TaskData>&
2955 taskData) {
2956 if (!err)
2957 {
2958 taskData->messages.emplace_back(
2959 messages::taskCompletedOK(
2960 std::to_string(taskData->index)));
2961 taskData->state = "Completed";
2962 }
2963 return task::completed;
2964 },
2965 taskMatchStr);
2966
2967 task->startTimer(std::chrono::minutes(5));
2968 task->populateResp(asyncResp->res);
2969 task->payload.emplace(std::move(payload));
2970 };
2971
2972 crow::connections::systemBus->async_method_call(
2973 std::move(collectCrashdumpCallback), crashdumpObject,
2974 crashdumpPath, iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002975 });
2976}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002977
Andrew Geisslercb92c032018-08-17 07:56:14 -07002978/**
2979 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2980 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002981inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002982{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002983 /**
2984 * Function handles POST method request.
2985 * The Clear Log actions does not require any parameter.The action deletes
2986 * all entries found in the Entries collection for this Log Service.
2987 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002988
George Liu0fda0f12021-11-16 10:06:17 +08002989 BMCWEB_ROUTE(
2990 app,
2991 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002992 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002993 .methods(boost::beast::http::verb::post)(
2994 [](const crow::Request&,
2995 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2996 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07002997
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002998 // Process response from Logging service.
2999 auto respHandler = [asyncResp](
3000 const boost::system::error_code ec) {
3001 BMCWEB_LOG_DEBUG
3002 << "doClearLog resp_handler callback: Done";
3003 if (ec)
3004 {
3005 // TODO Handle for specific error code
3006 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
3007 << ec;
3008 asyncResp->res.result(
3009 boost::beast::http::status::internal_server_error);
3010 return;
3011 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003012
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003013 asyncResp->res.result(
3014 boost::beast::http::status::no_content);
3015 };
3016
3017 // Make call to Logging service to request Clear Log
3018 crow::connections::systemBus->async_method_call(
3019 respHandler, "xyz.openbmc_project.Logging",
3020 "/xyz/openbmc_project/logging",
3021 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3022 });
3023}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003024
3025/****************************************************
3026 * Redfish PostCode interfaces
3027 * using DBUS interface: getPostCodesTS
3028 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003029inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003030{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003031 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003032 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08003033 .methods(
3034 boost::beast::http::verb::
3035 get)([](const crow::Request&,
3036 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3037 asyncResp->res.jsonValue = {
3038 {"@odata.id",
3039 "/redfish/v1/Systems/system/LogServices/PostCodes"},
3040 {"@odata.type", "#LogService.v1_1_0.LogService"},
3041 {"Name", "POST Code Log Service"},
3042 {"Description", "POST Code Log Service"},
3043 {"Id", "BIOS POST Code Log"},
3044 {"OverWritePolicy", "WrapsWhenFull"},
3045 {"Entries",
3046 {{"@odata.id",
3047 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05303048
George Liu0fda0f12021-11-16 10:06:17 +08003049 std::pair<std::string, std::string> redfishDateTimeOffset =
3050 crow::utility::getDateTimeOffsetNow();
3051 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3052 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3053 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303054
George Liu0fda0f12021-11-16 10:06:17 +08003055 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3056 {"target",
3057 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3058 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003059}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003060
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003061inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003062{
George Liu0fda0f12021-11-16 10:06:17 +08003063 BMCWEB_ROUTE(
3064 app,
3065 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003066 // The following privilege is incorrect; It should be ConfigureManager
3067 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003068 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003069 .methods(boost::beast::http::verb::post)(
3070 [](const crow::Request&,
3071 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3072 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003073
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003074 // Make call to post-code service to request clear all
3075 crow::connections::systemBus->async_method_call(
3076 [asyncResp](const boost::system::error_code ec) {
3077 if (ec)
3078 {
3079 // TODO Handle for specific error code
3080 BMCWEB_LOG_ERROR
3081 << "doClearPostCodes resp_handler got error "
3082 << ec;
3083 asyncResp->res.result(boost::beast::http::status::
3084 internal_server_error);
3085 messages::internalError(asyncResp->res);
3086 return;
3087 }
3088 },
3089 "xyz.openbmc_project.State.Boot.PostCode0",
3090 "/xyz/openbmc_project/State/Boot/PostCode0",
3091 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3092 });
3093}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003094
3095static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003096 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303097 const boost::container::flat_map<
3098 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003099 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3100 const uint64_t skip = 0, const uint64_t top = 0)
3101{
3102 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003103 const registries::Message* message =
3104 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003105
3106 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003107 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003108
3109 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303110 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3111 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003112 {
3113 currentCodeIndex++;
3114 std::string postcodeEntryID =
3115 "B" + std::to_string(bootIndex) + "-" +
3116 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3117
3118 uint64_t usecSinceEpoch = code.first;
3119 uint64_t usTimeOffset = 0;
3120
3121 if (1 == currentCodeIndex)
3122 { // already incremented
3123 firstCodeTimeUs = code.first;
3124 }
3125 else
3126 {
3127 usTimeOffset = code.first - firstCodeTimeUs;
3128 }
3129
3130 // skip if no specific codeIndex is specified and currentCodeIndex does
3131 // not fall between top and skip
3132 if ((codeIndex == 0) &&
3133 (currentCodeIndex <= skip || currentCodeIndex > top))
3134 {
3135 continue;
3136 }
3137
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003138 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003139 // currentIndex
3140 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3141 {
3142 // This is done for simplicity. 1st entry is needed to calculate
3143 // time offset. To improve efficiency, one can get to the entry
3144 // directly (possibly with flatmap's nth method)
3145 continue;
3146 }
3147
3148 // currentCodeIndex is within top and skip or equal to specified code
3149 // index
3150
3151 // Get the Created time from the timestamp
3152 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003153 entryTimeStr =
3154 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003155
3156 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3157 std::ostringstream hexCode;
3158 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303159 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003160 std::ostringstream timeOffsetStr;
3161 // Set Fixed -Point Notation
3162 timeOffsetStr << std::fixed;
3163 // Set precision to 4 digits
3164 timeOffsetStr << std::setprecision(4);
3165 // Add double to stream
3166 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3167 std::vector<std::string> messageArgs = {
3168 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3169
3170 // Get MessageArgs template from message registry
3171 std::string msg;
3172 if (message != nullptr)
3173 {
3174 msg = message->message;
3175
3176 // fill in this post code value
3177 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003178 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003179 {
3180 std::string argStr = "%" + std::to_string(++i);
3181 size_t argPos = msg.find(argStr);
3182 if (argPos != std::string::npos)
3183 {
3184 msg.replace(argPos, argStr.length(), messageArg);
3185 }
3186 }
3187 }
3188
Tim Leed4342a92020-04-27 11:47:58 +08003189 // Get Severity template from message registry
3190 std::string severity;
3191 if (message != nullptr)
3192 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003193 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003194 }
3195
ZhikuiRena3316fc2020-01-29 14:58:08 -08003196 // add to AsyncResp
3197 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003198 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003199 bmcLogEntry = {
3200 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3201 {"@odata.id",
3202 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3203 postcodeEntryID},
3204 {"Name", "POST Code Log Entry"},
3205 {"Id", postcodeEntryID},
3206 {"Message", std::move(msg)},
3207 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3208 {"MessageArgs", std::move(messageArgs)},
3209 {"EntryType", "Event"},
3210 {"Severity", std::move(severity)},
3211 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003212 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3213 {
3214 bmcLogEntry["AdditionalDataURI"] =
3215 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3216 postcodeEntryID + "/attachment";
3217 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003218 }
3219}
3220
zhanghch058d1b46d2021-04-01 11:18:24 +08003221static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003222 const uint16_t bootIndex,
3223 const uint64_t codeIndex)
3224{
3225 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303226 [aResp, bootIndex,
3227 codeIndex](const boost::system::error_code ec,
3228 const boost::container::flat_map<
3229 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3230 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003231 if (ec)
3232 {
3233 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3234 messages::internalError(aResp->res);
3235 return;
3236 }
3237
3238 // skip the empty postcode boots
3239 if (postcode.empty())
3240 {
3241 return;
3242 }
3243
3244 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3245
3246 aResp->res.jsonValue["Members@odata.count"] =
3247 aResp->res.jsonValue["Members"].size();
3248 },
Jonathan Doman15124762021-01-07 17:54:17 -08003249 "xyz.openbmc_project.State.Boot.PostCode0",
3250 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003251 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3252 bootIndex);
3253}
3254
zhanghch058d1b46d2021-04-01 11:18:24 +08003255static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003256 const uint16_t bootIndex,
3257 const uint16_t bootCount,
3258 const uint64_t entryCount, const uint64_t skip,
3259 const uint64_t top)
3260{
3261 crow::connections::systemBus->async_method_call(
3262 [aResp, bootIndex, bootCount, entryCount, skip,
3263 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303264 const boost::container::flat_map<
3265 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3266 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003267 if (ec)
3268 {
3269 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3270 messages::internalError(aResp->res);
3271 return;
3272 }
3273
3274 uint64_t endCount = entryCount;
3275 if (!postcode.empty())
3276 {
3277 endCount = entryCount + postcode.size();
3278
3279 if ((skip < endCount) && ((top + skip) > entryCount))
3280 {
3281 uint64_t thisBootSkip =
3282 std::max(skip, entryCount) - entryCount;
3283 uint64_t thisBootTop =
3284 std::min(top + skip, endCount) - entryCount;
3285
3286 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3287 thisBootSkip, thisBootTop);
3288 }
3289 aResp->res.jsonValue["Members@odata.count"] = endCount;
3290 }
3291
3292 // continue to previous bootIndex
3293 if (bootIndex < bootCount)
3294 {
3295 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3296 bootCount, endCount, skip, top);
3297 }
3298 else
3299 {
3300 aResp->res.jsonValue["Members@odata.nextLink"] =
George Liu0fda0f12021-11-16 10:06:17 +08003301 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
ZhikuiRena3316fc2020-01-29 14:58:08 -08003302 std::to_string(skip + top);
3303 }
3304 },
Jonathan Doman15124762021-01-07 17:54:17 -08003305 "xyz.openbmc_project.State.Boot.PostCode0",
3306 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003307 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3308 bootIndex);
3309}
3310
zhanghch058d1b46d2021-04-01 11:18:24 +08003311static void
3312 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3313 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003314{
3315 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003316 sdbusplus::asio::getProperty<uint16_t>(
3317 *crow::connections::systemBus,
3318 "xyz.openbmc_project.State.Boot.PostCode0",
3319 "/xyz/openbmc_project/State/Boot/PostCode0",
3320 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3321 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3322 const uint16_t bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003323 if (ec)
3324 {
3325 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3326 messages::internalError(aResp->res);
3327 return;
3328 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003329 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3330 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003331}
3332
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003333inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003334{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003335 BMCWEB_ROUTE(app,
3336 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003337 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003338 .methods(boost::beast::http::verb::get)(
3339 [](const crow::Request& req,
3340 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3341 asyncResp->res.jsonValue["@odata.type"] =
3342 "#LogEntryCollection.LogEntryCollection";
3343 asyncResp->res.jsonValue["@odata.id"] =
3344 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3345 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3346 asyncResp->res.jsonValue["Description"] =
3347 "Collection of POST Code Log Entries";
3348 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3349 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003350
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003351 uint64_t skip = 0;
3352 uint64_t top = maxEntriesPerPage; // Show max entries by default
3353 if (!getSkipParam(asyncResp, req, skip))
3354 {
3355 return;
3356 }
3357 if (!getTopParam(asyncResp, req, top))
3358 {
3359 return;
3360 }
3361 getCurrentBootNumber(asyncResp, skip, top);
3362 });
3363}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003364
George Liu647b3cd2021-07-05 12:43:56 +08003365/**
3366 * @brief Parse post code ID and get the current value and index value
3367 * eg: postCodeID=B1-2, currentValue=1, index=2
3368 *
3369 * @param[in] postCodeID Post Code ID
3370 * @param[out] currentValue Current value
3371 * @param[out] index Index value
3372 *
3373 * @return bool true if the parsing is successful, false the parsing fails
3374 */
3375inline static bool parsePostCode(const std::string& postCodeID,
3376 uint64_t& currentValue, uint16_t& index)
3377{
3378 std::vector<std::string> split;
3379 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3380 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3381 {
3382 return false;
3383 }
3384
Ed Tanousca45aa32022-01-07 09:28:45 -08003385 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003386 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003387 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003388 const char* end = split[0].data() + split[0].size();
3389 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3390
3391 if (ptrIndex != end || ecIndex != std::errc())
3392 {
3393 return false;
3394 }
3395
3396 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003397
3398 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003399 end = split[1].data() + split[1].size();
3400 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003401
Ed Tanousdcf2ebc2022-01-25 10:07:45 -08003402 return ptrValue == end && ecValue != std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003403}
3404
3405inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3406{
George Liu0fda0f12021-11-16 10:06:17 +08003407 BMCWEB_ROUTE(
3408 app,
3409 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003410 .privileges(redfish::privileges::getLogEntry)
3411 .methods(boost::beast::http::verb::get)(
3412 [](const crow::Request& req,
3413 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3414 const std::string& postCodeID) {
3415 if (!http_helpers::isOctetAccepted(
3416 req.getHeaderValue("Accept")))
3417 {
3418 asyncResp->res.result(
3419 boost::beast::http::status::bad_request);
3420 return;
3421 }
3422
3423 uint64_t currentValue = 0;
3424 uint16_t index = 0;
3425 if (!parsePostCode(postCodeID, currentValue, index))
3426 {
3427 messages::resourceNotFound(asyncResp->res, "LogEntry",
3428 postCodeID);
3429 return;
3430 }
3431
3432 crow::connections::systemBus->async_method_call(
3433 [asyncResp, postCodeID, currentValue](
3434 const boost::system::error_code ec,
3435 const std::vector<std::tuple<
3436 uint64_t, std::vector<uint8_t>>>& postcodes) {
3437 if (ec.value() == EBADR)
3438 {
3439 messages::resourceNotFound(asyncResp->res,
3440 "LogEntry", postCodeID);
3441 return;
3442 }
3443 if (ec)
3444 {
3445 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3446 messages::internalError(asyncResp->res);
3447 return;
3448 }
3449
3450 size_t value = static_cast<size_t>(currentValue) - 1;
3451 if (value == std::string::npos ||
3452 postcodes.size() < currentValue)
3453 {
3454 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3455 messages::resourceNotFound(asyncResp->res,
3456 "LogEntry", postCodeID);
3457 return;
3458 }
3459
Ed Tanous9eb808c2022-01-25 10:19:23 -08003460 const auto& [tID, c] = postcodes[value];
Ed Tanous46ff87b2022-01-07 09:25:51 -08003461 if (c.empty())
George Liu647b3cd2021-07-05 12:43:56 +08003462 {
3463 BMCWEB_LOG_INFO << "No found post code data";
3464 messages::resourceNotFound(asyncResp->res,
3465 "LogEntry", postCodeID);
3466 return;
3467 }
Ed Tanous46ff87b2022-01-07 09:25:51 -08003468 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3469 const char* d = reinterpret_cast<const char*>(c.data());
3470 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003471
3472 asyncResp->res.addHeader("Content-Type",
3473 "application/octet-stream");
3474 asyncResp->res.addHeader("Content-Transfer-Encoding",
3475 "Base64");
3476 asyncResp->res.body() =
3477 crow::utility::base64encode(strData);
3478 },
3479 "xyz.openbmc_project.State.Boot.PostCode0",
3480 "/xyz/openbmc_project/State/Boot/PostCode0",
3481 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3482 index);
3483 });
3484}
3485
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003486inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003487{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003488 BMCWEB_ROUTE(
3489 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003490 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003491 .methods(boost::beast::http::verb::get)(
Ed Tanousace85d62021-10-26 12:45:59 -07003492 [](const crow::Request& req,
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003493 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3494 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003495 uint16_t bootIndex = 0;
3496 uint64_t codeIndex = 0;
3497 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003498 {
3499 // Requested ID was not found
Ed Tanousace85d62021-10-26 12:45:59 -07003500 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003501 return;
3502 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003503 if (bootIndex == 0 || codeIndex == 0)
3504 {
3505 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3506 << targetID;
3507 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003508
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003509 asyncResp->res.jsonValue["@odata.type"] =
3510 "#LogEntry.v1_4_0.LogEntry";
3511 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08003512 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003513 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3514 asyncResp->res.jsonValue["Description"] =
3515 "Collection of POST Code Log Entries";
3516 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3517 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003518
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003519 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3520 });
3521}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003522
Ed Tanous1da66f72018-07-27 16:13:37 -07003523} // namespace redfish