blob: aa4d5ec49e945646aca0b2f421770e0d34969508 [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
Jason M. Bills4851d452019-03-28 11:27:48 -070059namespace message_registries
60{
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) {
68 return !std::strcmp(messageEntry.first, messageKey.c_str());
69 });
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}
102} // namespace message_registries
103
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('_');
Jason M. Bills16428a12018-11-02 12:42:29 -0700321 if (underscorePos != tsStr.npos)
322 {
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 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800331 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700332 return false;
333 }
334 }
335 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700336 auto [ptr, ec] =
337 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
338 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700339 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800340 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700341 return false;
342 }
343 return true;
344}
345
Jason M. Bills95820182019-04-22 16:25:34 -0700346static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500347 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700348{
349 static const std::filesystem::path redfishLogDir = "/var/log";
350 static const std::string redfishLogFilename = "redfish";
351
352 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500353 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700354 std::filesystem::directory_iterator(redfishLogDir))
355 {
356 // If we find a redfish log file, save the path
357 std::string filename = dirEnt.path().filename();
358 if (boost::starts_with(filename, redfishLogFilename))
359 {
360 redfishLogFiles.emplace_back(redfishLogDir / filename);
361 }
362 }
363 // As the log files rotate, they are appended with a ".#" that is higher for
364 // the older logs. Since we don't expect more than 10 log files, we
365 // can just sort the list to get them in order from newest to oldest
366 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
367
368 return !redfishLogFiles.empty();
369}
370
zhanghch058d1b46d2021-04-01 11:18:24 +0800371inline void
372 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
373 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500374{
375 std::string dumpPath;
376 if (dumpType == "BMC")
377 {
378 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
379 }
380 else if (dumpType == "System")
381 {
382 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
383 }
384 else
385 {
386 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
387 messages::internalError(asyncResp->res);
388 return;
389 }
390
391 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800392 [asyncResp, dumpPath,
393 dumpType](const boost::system::error_code ec,
394 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500395 if (ec)
396 {
397 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
398 messages::internalError(asyncResp->res);
399 return;
400 }
401
402 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
403 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500404 std::string dumpEntryPath =
405 "/xyz/openbmc_project/dump/" +
406 std::string(boost::algorithm::to_lower_copy(dumpType)) +
407 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500408
409 for (auto& object : resp)
410 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500411 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500412 {
413 continue;
414 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800415 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500416 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500417 std::string dumpStatus;
418 nlohmann::json thisEntry;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000419
420 std::string entryID = object.first.filename();
421 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500422 {
423 continue;
424 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500425
426 for (auto& interfaceMap : object.second)
427 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500428 if (interfaceMap.first ==
429 "xyz.openbmc_project.Common.Progress")
430 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800431 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500432 {
433 if (propertyMap.first == "Status")
434 {
435 auto status = std::get_if<std::string>(
436 &propertyMap.second);
437 if (status == nullptr)
438 {
439 messages::internalError(asyncResp->res);
440 break;
441 }
442 dumpStatus = *status;
443 }
444 }
445 }
446 else if (interfaceMap.first ==
447 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500448 {
449
450 for (auto& propertyMap : interfaceMap.second)
451 {
452 if (propertyMap.first == "Size")
453 {
454 auto sizePtr =
455 std::get_if<uint64_t>(&propertyMap.second);
456 if (sizePtr == nullptr)
457 {
458 messages::internalError(asyncResp->res);
459 break;
460 }
461 size = *sizePtr;
462 break;
463 }
464 }
465 }
466 else if (interfaceMap.first ==
467 "xyz.openbmc_project.Time.EpochTime")
468 {
469
Ed Tanous9eb808c2022-01-25 10:19:23 -0800470 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500471 {
472 if (propertyMap.first == "Elapsed")
473 {
474 const uint64_t* usecsTimeStamp =
475 std::get_if<uint64_t>(&propertyMap.second);
476 if (usecsTimeStamp == nullptr)
477 {
478 messages::internalError(asyncResp->res);
479 break;
480 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800481 timestamp = (*usecsTimeStamp / 1000 / 1000);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500482 break;
483 }
484 }
485 }
486 }
487
George Liu0fda0f12021-11-16 10:06:17 +0800488 if (dumpStatus !=
489 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500490 !dumpStatus.empty())
491 {
492 // Dump status is not Complete, no need to enumerate
493 continue;
494 }
495
George Liu647b3cd2021-07-05 12:43:56 +0800496 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500497 thisEntry["@odata.id"] = dumpPath + entryID;
498 thisEntry["Id"] = entryID;
499 thisEntry["EntryType"] = "Event";
Nan Zhou1d8782e2021-11-29 22:23:18 -0800500 thisEntry["Created"] =
501 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500502 thisEntry["Name"] = dumpType + " Dump Entry";
503
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500504 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500505
506 if (dumpType == "BMC")
507 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500508 thisEntry["DiagnosticDataType"] = "Manager";
509 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500510 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
511 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500512 }
513 else if (dumpType == "System")
514 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500515 thisEntry["DiagnosticDataType"] = "OEM";
516 thisEntry["OEMDiagnosticDataType"] = "System";
517 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500518 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
519 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500520 }
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500521 entriesArray.push_back(std::move(thisEntry));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500522 }
523 asyncResp->res.jsonValue["Members@odata.count"] =
524 entriesArray.size();
525 },
526 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
527 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
528}
529
zhanghch058d1b46d2021-04-01 11:18:24 +0800530inline void
531 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
532 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500533{
534 std::string dumpPath;
535 if (dumpType == "BMC")
536 {
537 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
538 }
539 else if (dumpType == "System")
540 {
541 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
542 }
543 else
544 {
545 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
546 messages::internalError(asyncResp->res);
547 return;
548 }
549
550 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800551 [asyncResp, entryID, dumpPath,
552 dumpType](const boost::system::error_code ec,
553 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500554 if (ec)
555 {
556 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
557 messages::internalError(asyncResp->res);
558 return;
559 }
560
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500561 bool foundDumpEntry = false;
562 std::string dumpEntryPath =
563 "/xyz/openbmc_project/dump/" +
564 std::string(boost::algorithm::to_lower_copy(dumpType)) +
565 "/entry/";
566
Ed Tanous9eb808c2022-01-25 10:19:23 -0800567 for (const auto& objectPath : resp)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500568 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500569 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500570 {
571 continue;
572 }
573
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500574 foundDumpEntry = true;
Nan Zhou1d8782e2021-11-29 22:23:18 -0800575 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500576 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500577 std::string dumpStatus;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500578
Ed Tanous9eb808c2022-01-25 10:19:23 -0800579 for (const auto& interfaceMap : objectPath.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500580 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500581 if (interfaceMap.first ==
582 "xyz.openbmc_project.Common.Progress")
583 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800584 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500585 {
586 if (propertyMap.first == "Status")
587 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800588 const std::string* status =
589 std::get_if<std::string>(
590 &propertyMap.second);
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500591 if (status == nullptr)
592 {
593 messages::internalError(asyncResp->res);
594 break;
595 }
596 dumpStatus = *status;
597 }
598 }
599 }
600 else if (interfaceMap.first ==
601 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500602 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800603 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500604 {
605 if (propertyMap.first == "Size")
606 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800607 const uint64_t* sizePtr =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500608 std::get_if<uint64_t>(&propertyMap.second);
609 if (sizePtr == nullptr)
610 {
611 messages::internalError(asyncResp->res);
612 break;
613 }
614 size = *sizePtr;
615 break;
616 }
617 }
618 }
619 else if (interfaceMap.first ==
620 "xyz.openbmc_project.Time.EpochTime")
621 {
Ed Tanous9eb808c2022-01-25 10:19:23 -0800622 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500623 {
624 if (propertyMap.first == "Elapsed")
625 {
626 const uint64_t* usecsTimeStamp =
627 std::get_if<uint64_t>(&propertyMap.second);
628 if (usecsTimeStamp == nullptr)
629 {
630 messages::internalError(asyncResp->res);
631 break;
632 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800633 timestamp = *usecsTimeStamp / 1000 / 1000;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500634 break;
635 }
636 }
637 }
638 }
639
George Liu0fda0f12021-11-16 10:06:17 +0800640 if (dumpStatus !=
641 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500642 !dumpStatus.empty())
643 {
644 // Dump status is not Complete
645 // return not found until status is changed to Completed
646 messages::resourceNotFound(asyncResp->res,
647 dumpType + " dump", entryID);
648 return;
649 }
650
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500651 asyncResp->res.jsonValue["@odata.type"] =
George Liu647b3cd2021-07-05 12:43:56 +0800652 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500653 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
654 asyncResp->res.jsonValue["Id"] = entryID;
655 asyncResp->res.jsonValue["EntryType"] = "Event";
656 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800657 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500658 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
659
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500660 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500661
662 if (dumpType == "BMC")
663 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500664 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
665 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500666 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
667 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500668 }
669 else if (dumpType == "System")
670 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500671 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
672 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500673 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500674 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500675 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
676 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500677 }
678 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500679 if (foundDumpEntry == false)
680 {
681 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
682 messages::internalError(asyncResp->res);
683 return;
684 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500685 },
686 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
687 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
688}
689
zhanghch058d1b46d2021-04-01 11:18:24 +0800690inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800691 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500692 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500693{
George Liu3de8d8b2021-03-22 17:49:39 +0800694 auto respHandler = [asyncResp,
695 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500696 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
697 if (ec)
698 {
George Liu3de8d8b2021-03-22 17:49:39 +0800699 if (ec.value() == EBADR)
700 {
701 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
702 return;
703 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500704 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
705 << ec;
706 messages::internalError(asyncResp->res);
707 return;
708 }
709 };
710 crow::connections::systemBus->async_method_call(
711 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500712 "/xyz/openbmc_project/dump/" +
713 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
714 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500715 "xyz.openbmc_project.Object.Delete", "Delete");
716}
717
zhanghch058d1b46d2021-04-01 11:18:24 +0800718inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700719 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800720 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
721 const uint32_t& dumpId, const std::string& dumpPath,
722 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500723{
724 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500725 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500726 boost::system::error_code err, sdbusplus::message::message& m,
727 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000728 if (err)
729 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500730 BMCWEB_LOG_ERROR << "Error in creating a dump";
731 taskData->state = "Cancelled";
732 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000733 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500734 std::vector<std::pair<
Ed Tanous168e20c2021-12-13 14:39:53 -0800735 std::string, std::vector<std::pair<
736 std::string, dbus::utility::DbusVariantType>>>>
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500737 interfacesList;
738
739 sdbusplus::message::object_path objPath;
740
741 m.read(objPath, interfacesList);
742
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500743 if (objPath.str ==
744 "/xyz/openbmc_project/dump/" +
745 std::string(boost::algorithm::to_lower_copy(dumpType)) +
746 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500747 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500748 nlohmann::json retMessage = messages::success();
749 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500750
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500751 std::string headerLoc =
752 "Location: " + dumpPath + std::to_string(dumpId);
753 taskData->payload->httpHeaders.emplace_back(
754 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500755
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500756 taskData->state = "Completed";
757 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500758 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500759 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500760 },
761 "type='signal',interface='org.freedesktop.DBus."
762 "ObjectManager',"
763 "member='InterfacesAdded', "
764 "path='/xyz/openbmc_project/dump'");
765
766 task->startTimer(std::chrono::minutes(3));
767 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700768 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500769}
770
zhanghch058d1b46d2021-04-01 11:18:24 +0800771inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
772 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500773{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500774
775 std::string dumpPath;
776 if (dumpType == "BMC")
777 {
778 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
779 }
780 else if (dumpType == "System")
781 {
782 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
783 }
784 else
785 {
786 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
787 messages::internalError(asyncResp->res);
788 return;
789 }
790
791 std::optional<std::string> diagnosticDataType;
792 std::optional<std::string> oemDiagnosticDataType;
793
794 if (!redfish::json_util::readJson(
795 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
796 "OEMDiagnosticDataType", oemDiagnosticDataType))
797 {
798 return;
799 }
800
801 if (dumpType == "System")
802 {
803 if (!oemDiagnosticDataType || !diagnosticDataType)
804 {
805 BMCWEB_LOG_ERROR << "CreateDump action parameter "
806 "'DiagnosticDataType'/"
807 "'OEMDiagnosticDataType' value not found!";
808 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";
817 messages::invalidObject(asyncResp->res,
818 "System Dump creation parameters");
819 return;
820 }
821 }
822 else if (dumpType == "BMC")
823 {
824 if (!diagnosticDataType)
825 {
George Liu0fda0f12021-11-16 10:06:17 +0800826 BMCWEB_LOG_ERROR
827 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500828 messages::actionParameterMissing(
829 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
830 return;
831 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700832 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500833 {
834 BMCWEB_LOG_ERROR
835 << "Wrong parameter value passed for 'DiagnosticDataType'";
836 messages::invalidObject(asyncResp->res,
837 "BMC Dump creation parameters");
838 return;
839 }
840 }
841
842 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700843 [asyncResp, payload(task::Payload(req)), dumpPath,
844 dumpType](const boost::system::error_code ec,
845 const uint32_t& dumpId) mutable {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500846 if (ec)
847 {
848 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
849 messages::internalError(asyncResp->res);
850 return;
851 }
852 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
853
Ed Tanous98be3e32021-09-16 15:05:36 -0700854 createDumpTaskCallback(std::move(payload), asyncResp, dumpId,
855 dumpPath, dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500856 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500857 "xyz.openbmc_project.Dump.Manager",
858 "/xyz/openbmc_project/dump/" +
859 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500860 "xyz.openbmc_project.Dump.Create", "CreateDump");
861}
862
zhanghch058d1b46d2021-04-01 11:18:24 +0800863inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
864 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500865{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500866 std::string dumpTypeLowerCopy =
867 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800868
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500869 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500870 [asyncResp, dumpType](const boost::system::error_code ec,
871 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500872 if (ec)
873 {
874 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
875 messages::internalError(asyncResp->res);
876 return;
877 }
878
879 for (const std::string& path : subTreePaths)
880 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000881 sdbusplus::message::object_path objPath(path);
882 std::string logID = objPath.filename();
883 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500884 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000885 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500886 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000887 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500888 }
889 },
890 "xyz.openbmc_project.ObjectMapper",
891 "/xyz/openbmc_project/object_mapper",
892 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500893 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
894 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
895 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500896}
897
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700898inline static void parseCrashdumpParameters(
Ed Tanous168e20c2021-12-13 14:39:53 -0800899 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
900 params,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500901 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700902{
903 for (auto property : params)
904 {
905 if (property.first == "Timestamp")
906 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500907 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500908 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700909 if (value != nullptr)
910 {
911 timestamp = *value;
912 }
913 }
914 else if (property.first == "Filename")
915 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500916 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500917 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700918 if (value != nullptr)
919 {
920 filename = *value;
921 }
922 }
923 else if (property.first == "Log")
924 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500925 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500926 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700927 if (value != nullptr)
928 {
929 logfile = *value;
930 }
931 }
932 }
933}
934
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500935constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700936inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700937{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800938 /**
939 * Functions triggers appropriate requests on DBus
940 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700941 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700942 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700943 .methods(boost::beast::http::verb::get)(
944 [](const crow::Request&,
945 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
946
947 {
948 // Collections don't include the static data added by SubRoute
949 // because it has a duplicate entry for members
950 asyncResp->res.jsonValue["@odata.type"] =
951 "#LogServiceCollection.LogServiceCollection";
952 asyncResp->res.jsonValue["@odata.id"] =
953 "/redfish/v1/Systems/system/LogServices";
954 asyncResp->res.jsonValue["Name"] =
955 "System Log Services Collection";
956 asyncResp->res.jsonValue["Description"] =
957 "Collection of LogServices for this Computer System";
958 nlohmann::json& logServiceArray =
959 asyncResp->res.jsonValue["Members"];
960 logServiceArray = nlohmann::json::array();
961 logServiceArray.push_back(
962 {{"@odata.id",
963 "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500964#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700965 logServiceArray.push_back(
966 {{"@odata.id",
967 "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600968#endif
969
Jason M. Billsd53dd412019-02-12 17:16:22 -0800970#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971 logServiceArray.push_back(
972 {{"@odata.id",
973 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800974#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800975
976#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
977 logServiceArray.push_back(
978 {{"@odata.id",
979 "/redfish/v1/Systems/system/LogServices/HostLogger"}});
980#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700981 asyncResp->res.jsonValue["Members@odata.count"] =
982 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800983
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700984 crow::connections::systemBus->async_method_call(
985 [asyncResp](const boost::system::error_code ec,
986 const std::vector<std::string>& subtreePath) {
987 if (ec)
988 {
989 BMCWEB_LOG_ERROR << ec;
990 return;
991 }
ZhikuiRena3316fc2020-01-29 14:58:08 -0800992
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700993 for (auto& pathStr : subtreePath)
994 {
995 if (pathStr.find("PostCode") != std::string::npos)
996 {
997 nlohmann::json& logServiceArrayLocal =
998 asyncResp->res.jsonValue["Members"];
999 logServiceArrayLocal.push_back(
George Liu0fda0f12021-11-16 10:06:17 +08001000 {{"@odata.id",
1001 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001002 asyncResp->res
1003 .jsonValue["Members@odata.count"] =
1004 logServiceArrayLocal.size();
1005 return;
1006 }
1007 }
1008 },
1009 "xyz.openbmc_project.ObjectMapper",
1010 "/xyz/openbmc_project/object_mapper",
1011 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
1012 0, std::array<const char*, 1>{postCodeIface});
1013 });
1014}
1015
1016inline void requestRoutesEventLogService(App& app)
1017{
1018 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -07001019 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001020 .methods(
1021 boost::beast::http::verb::
1022 get)([](const crow::Request&,
1023 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1024 asyncResp->res.jsonValue["@odata.id"] =
1025 "/redfish/v1/Systems/system/LogServices/EventLog";
1026 asyncResp->res.jsonValue["@odata.type"] =
1027 "#LogService.v1_1_0.LogService";
1028 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1029 asyncResp->res.jsonValue["Description"] =
1030 "System Event Log Service";
1031 asyncResp->res.jsonValue["Id"] = "EventLog";
1032 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301033
1034 std::pair<std::string, std::string> redfishDateTimeOffset =
1035 crow::utility::getDateTimeOffsetNow();
1036
1037 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1038 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1039 redfishDateTimeOffset.second;
1040
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001041 asyncResp->res.jsonValue["Entries"] = {
1042 {"@odata.id",
1043 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1044 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1045
George Liu0fda0f12021-11-16 10:06:17 +08001046 {"target",
1047 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001048 });
1049}
1050
1051inline void requestRoutesJournalEventLogClear(App& app)
1052{
1053 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1054 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001055 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001056 .methods(boost::beast::http::verb::post)(
1057 [](const crow::Request&,
1058 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1059 // Clear the EventLog by deleting the log files
1060 std::vector<std::filesystem::path> redfishLogFiles;
1061 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001062 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001063 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001064 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001065 std::error_code ec;
1066 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001067 }
1068 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001069
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001070 // Reload rsyslog so it knows to start new log files
1071 crow::connections::systemBus->async_method_call(
1072 [asyncResp](const boost::system::error_code ec) {
1073 if (ec)
1074 {
1075 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1076 << ec;
1077 messages::internalError(asyncResp->res);
1078 return;
1079 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001080
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001081 messages::success(asyncResp->res);
1082 },
1083 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1084 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1085 "rsyslog.service", "replace");
1086 });
1087}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001088
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001089static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001090 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001091 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001092{
Jason M. Bills95820182019-04-22 16:25:34 -07001093 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001094 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001095 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001096 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001097 {
1098 return 1;
1099 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001100 std::string timestamp = logEntry.substr(0, space);
1101 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001102 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001103 if (entryStart == std::string::npos)
1104 {
1105 return 1;
1106 }
1107 std::string_view entry(logEntry);
1108 entry.remove_prefix(entryStart);
1109 // Use split to separate the entry into its fields
1110 std::vector<std::string> logEntryFields;
1111 boost::split(logEntryFields, entry, boost::is_any_of(","),
1112 boost::token_compress_on);
1113 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001114 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001115 {
1116 return 1;
1117 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001118 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001119
Jason M. Bills4851d452019-03-28 11:27:48 -07001120 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001121 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001122 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001123
Jason M. Bills4851d452019-03-28 11:27:48 -07001124 std::string msg;
1125 std::string severity;
1126 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001127 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001128 msg = message->message;
1129 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001130 }
1131
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001132 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001133 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001134 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001135 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001136 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001137 // If the first string is empty, assume there are no MessageArgs
1138 std::size_t messageArgsSize = 0;
1139 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001140 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001141 messageArgsSize = logEntryFields.size() - 1;
1142 }
1143
Ed Tanous23a21a12020-07-25 04:45:05 +00001144 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001145
1146 // Fill the MessageArgs into the Message
1147 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001148 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001149 {
1150 std::string argStr = "%" + std::to_string(++i);
1151 size_t argPos = msg.find(argStr);
1152 if (argPos != std::string::npos)
1153 {
1154 msg.replace(argPos, argStr.length(), messageArg);
1155 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001156 }
1157 }
1158
Jason M. Bills95820182019-04-22 16:25:34 -07001159 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1160 // format which matches the Redfish format except for the fractional seconds
1161 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001162 std::size_t dot = timestamp.find_first_of('.');
1163 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001164 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001165 {
Jason M. Bills95820182019-04-22 16:25:34 -07001166 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001167 }
1168
1169 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001170 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001171 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001172 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001173 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001174 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001175 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001176 {"Id", logEntryID},
1177 {"Message", std::move(msg)},
1178 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001179 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001180 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001181 {"Severity", std::move(severity)},
1182 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001183 return 0;
1184}
1185
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001186inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001187{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001188 BMCWEB_ROUTE(app,
1189 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001190 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001191 .methods(boost::beast::http::verb::get)(
1192 [](const crow::Request& req,
1193 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1194 uint64_t skip = 0;
1195 uint64_t top = maxEntriesPerPage; // Show max entries by default
1196 if (!getSkipParam(asyncResp, req, skip))
Jason M. Bills95820182019-04-22 16:25:34 -07001197 {
Jason M. Bills95820182019-04-22 16:25:34 -07001198 return;
1199 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001200 if (!getTopParam(asyncResp, req, top))
Jason M. Bills897967d2019-07-29 17:05:30 -07001201 {
Jason M. Bills897967d2019-07-29 17:05:30 -07001202 return;
1203 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001204 // Collections don't include the static data added by SubRoute
1205 // because it has a duplicate entry for members
1206 asyncResp->res.jsonValue["@odata.type"] =
1207 "#LogEntryCollection.LogEntryCollection";
1208 asyncResp->res.jsonValue["@odata.id"] =
1209 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1210 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1211 asyncResp->res.jsonValue["Description"] =
1212 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001213
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001214 nlohmann::json& logEntryArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001215 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001216 logEntryArray = nlohmann::json::array();
1217 // Go through the log files and create a unique ID for each
1218 // entry
1219 std::vector<std::filesystem::path> redfishLogFiles;
1220 getRedfishLogFiles(redfishLogFiles);
1221 uint64_t entryCount = 0;
1222 std::string logEntry;
1223
1224 // Oldest logs are in the last file, so start there and loop
1225 // backwards
1226 for (auto it = redfishLogFiles.rbegin();
1227 it < redfishLogFiles.rend(); it++)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001228 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001229 std::ifstream logStream(*it);
1230 if (!logStream.is_open())
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001231 {
1232 continue;
1233 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001234
1235 // Reset the unique ID on the first entry
1236 bool firstEntry = true;
1237 while (std::getline(logStream, logEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001238 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001239 entryCount++;
1240 // Handle paging using skip (number of entries to skip
1241 // from the start) and top (number of entries to
1242 // display)
1243 if (entryCount <= skip || entryCount > skip + top)
George Liuebd45902020-08-26 14:21:10 +08001244 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001245 continue;
George Liuebd45902020-08-26 14:21:10 +08001246 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001247
1248 std::string idStr;
1249 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
George Liuebd45902020-08-26 14:21:10 +08001250 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001251 continue;
George Liuebd45902020-08-26 14:21:10 +08001252 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001253
1254 if (firstEntry)
1255 {
1256 firstEntry = false;
1257 }
1258
1259 logEntryArray.push_back({});
1260 nlohmann::json& bmcLogEntry = logEntryArray.back();
1261 if (fillEventLogEntryJson(idStr, logEntry,
1262 bmcLogEntry) != 0)
Xiaochao Ma75710de2021-01-21 17:56:02 +08001263 {
1264 messages::internalError(asyncResp->res);
1265 return;
1266 }
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001267 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001268 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001269 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1270 if (skip + top < entryCount)
Ed Tanous271584a2019-07-09 16:24:22 -07001271 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001272 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001273 "/redfish/v1/Systems/system/LogServices/EventLog/"
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001274 "Entries?$skip=" +
1275 std::to_string(skip + top);
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001276 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001277 });
1278}
Chicago Duan336e96c2019-07-15 14:22:08 +08001279
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001280inline void requestRoutesJournalEventLogEntry(App& app)
1281{
1282 BMCWEB_ROUTE(
1283 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001284 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001285 .methods(boost::beast::http::verb::get)(
1286 [](const crow::Request&,
1287 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1288 const std::string& param) {
1289 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001290
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001291 // Go through the log files and check the unique ID for each
1292 // entry to find the target entry
1293 std::vector<std::filesystem::path> redfishLogFiles;
1294 getRedfishLogFiles(redfishLogFiles);
1295 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001296
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001297 // Oldest logs are in the last file, so start there and loop
1298 // backwards
1299 for (auto it = redfishLogFiles.rbegin();
1300 it < redfishLogFiles.rend(); it++)
1301 {
1302 std::ifstream logStream(*it);
1303 if (!logStream.is_open())
1304 {
1305 continue;
1306 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001307
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001308 // Reset the unique ID on the first entry
1309 bool firstEntry = true;
1310 while (std::getline(logStream, logEntry))
1311 {
1312 std::string idStr;
1313 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1314 {
1315 continue;
1316 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001317
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001318 if (firstEntry)
1319 {
1320 firstEntry = false;
1321 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001322
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001323 if (idStr == targetID)
1324 {
1325 if (fillEventLogEntryJson(
1326 idStr, logEntry,
1327 asyncResp->res.jsonValue) != 0)
1328 {
1329 messages::internalError(asyncResp->res);
1330 return;
1331 }
1332 return;
1333 }
1334 }
1335 }
1336 // Requested ID was not found
1337 messages::resourceMissingAtURI(asyncResp->res, targetID);
1338 });
1339}
1340
1341inline void requestRoutesDBusEventLogEntryCollection(App& app)
1342{
1343 BMCWEB_ROUTE(app,
1344 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001345 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001346 .methods(
1347 boost::beast::http::verb::
1348 get)([](const crow::Request&,
1349 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1350 // Collections don't include the static data added by SubRoute
1351 // because it has a duplicate entry for members
1352 asyncResp->res.jsonValue["@odata.type"] =
1353 "#LogEntryCollection.LogEntryCollection";
1354 asyncResp->res.jsonValue["@odata.id"] =
1355 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1356 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1357 asyncResp->res.jsonValue["Description"] =
1358 "Collection of System Event Log Entries";
1359
1360 // DBus implementation of EventLog/Entries
1361 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001362 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001363 [asyncResp](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001364 const dbus::utility::ManagedObjectType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001365 if (ec)
1366 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001367 // TODO Handle for specific error code
1368 BMCWEB_LOG_ERROR
1369 << "getLogEntriesIfaceData resp_handler got error "
1370 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001371 messages::internalError(asyncResp->res);
1372 return;
1373 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001374 nlohmann::json& entriesArray =
1375 asyncResp->res.jsonValue["Members"];
1376 entriesArray = nlohmann::json::array();
Ed Tanous9eb808c2022-01-25 10:19:23 -08001377 for (const auto& objectPath : resp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001378 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001379 const uint32_t* id = nullptr;
Ed Tanousc419c752022-01-26 12:19:54 -08001380 const uint64_t* timestamp = nullptr;
1381 const uint64_t* updateTimestamp = nullptr;
Ed Tanous914e2d52022-01-07 11:38:34 -08001382 const std::string* severity = nullptr;
1383 const std::string* message = nullptr;
1384 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001385 bool resolved = false;
Ed Tanous9eb808c2022-01-25 10:19:23 -08001386 for (const auto& interfaceMap : objectPath.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001387 {
1388 if (interfaceMap.first ==
1389 "xyz.openbmc_project.Logging.Entry")
1390 {
Ed Tanous9eb808c2022-01-25 10:19:23 -08001391 for (const auto& propertyMap :
1392 interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001393 {
1394 if (propertyMap.first == "Id")
1395 {
1396 id = std::get_if<uint32_t>(
1397 &propertyMap.second);
1398 }
1399 else if (propertyMap.first == "Timestamp")
1400 {
Ed Tanousc419c752022-01-26 12:19:54 -08001401 timestamp = std::get_if<uint64_t>(
1402 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001403 }
1404 else if (propertyMap.first ==
1405 "UpdateTimestamp")
1406 {
Ed Tanousc419c752022-01-26 12:19:54 -08001407 updateTimestamp = std::get_if<uint64_t>(
1408 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001409 }
1410 else if (propertyMap.first == "Severity")
1411 {
1412 severity = std::get_if<std::string>(
1413 &propertyMap.second);
1414 }
1415 else if (propertyMap.first == "Message")
1416 {
1417 message = std::get_if<std::string>(
1418 &propertyMap.second);
1419 }
1420 else if (propertyMap.first == "Resolved")
1421 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001422 const bool* resolveptr =
1423 std::get_if<bool>(
1424 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001425 if (resolveptr == nullptr)
1426 {
1427 messages::internalError(
1428 asyncResp->res);
1429 return;
1430 }
1431 resolved = *resolveptr;
1432 }
1433 }
1434 if (id == nullptr || message == nullptr ||
1435 severity == nullptr)
1436 {
1437 messages::internalError(asyncResp->res);
1438 return;
1439 }
1440 }
1441 else if (interfaceMap.first ==
1442 "xyz.openbmc_project.Common.FilePath")
1443 {
Ed Tanous9eb808c2022-01-25 10:19:23 -08001444 for (const auto& propertyMap :
1445 interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001446 {
1447 if (propertyMap.first == "Path")
1448 {
1449 filePath = std::get_if<std::string>(
1450 &propertyMap.second);
1451 }
1452 }
1453 }
1454 }
1455 // Object path without the
1456 // xyz.openbmc_project.Logging.Entry interface, ignore
1457 // and continue.
1458 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001459 severity == nullptr || timestamp == nullptr ||
1460 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001461 {
1462 continue;
1463 }
1464 entriesArray.push_back({});
1465 nlohmann::json& thisEntry = entriesArray.back();
1466 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1467 thisEntry["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001468 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001469 std::to_string(*id);
1470 thisEntry["Name"] = "System Event Log Entry";
1471 thisEntry["Id"] = std::to_string(*id);
1472 thisEntry["Message"] = *message;
1473 thisEntry["Resolved"] = resolved;
1474 thisEntry["EntryType"] = "Event";
1475 thisEntry["Severity"] =
1476 translateSeverityDbusToRedfish(*severity);
1477 thisEntry["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001478 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001479 thisEntry["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001480 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001481 if (filePath != nullptr)
1482 {
1483 thisEntry["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001484 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001485 std::to_string(*id) + "/attachment";
1486 }
1487 }
1488 std::sort(entriesArray.begin(), entriesArray.end(),
1489 [](const nlohmann::json& left,
1490 const nlohmann::json& right) {
1491 return (left["Id"] <= right["Id"]);
1492 });
1493 asyncResp->res.jsonValue["Members@odata.count"] =
1494 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001495 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001496 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1497 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1498 });
1499}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001500
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001501inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001502{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001503 BMCWEB_ROUTE(
1504 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001505 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001506 .methods(boost::beast::http::verb::get)(
1507 [](const crow::Request&,
1508 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1509 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001510
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001511 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001512 std::string entryID = param;
1513 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001514
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001515 // DBus implementation of EventLog/Entries
1516 // Make call to Logging Service to find all log entry objects
1517 crow::connections::systemBus->async_method_call(
1518 [asyncResp, entryID](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001519 const GetManagedPropertyType& resp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001520 if (ec.value() == EBADR)
1521 {
1522 messages::resourceNotFound(
1523 asyncResp->res, "EventLogEntry", entryID);
1524 return;
1525 }
1526 if (ec)
1527 {
George Liu0fda0f12021-11-16 10:06:17 +08001528 BMCWEB_LOG_ERROR
1529 << "EventLogEntry (DBus) resp_handler got error "
1530 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001531 messages::internalError(asyncResp->res);
1532 return;
1533 }
Ed Tanous914e2d52022-01-07 11:38:34 -08001534 const uint32_t* id = nullptr;
Ed Tanousc419c752022-01-26 12:19:54 -08001535 const uint64_t* timestamp = nullptr;
1536 const uint64_t* updateTimestamp = nullptr;
Ed Tanous914e2d52022-01-07 11:38:34 -08001537 const std::string* severity = nullptr;
1538 const std::string* message = nullptr;
1539 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001540 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001541
Ed Tanous9eb808c2022-01-25 10:19:23 -08001542 for (const auto& propertyMap : resp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001543 {
1544 if (propertyMap.first == "Id")
1545 {
1546 id = std::get_if<uint32_t>(&propertyMap.second);
1547 }
1548 else if (propertyMap.first == "Timestamp")
1549 {
Ed Tanousc419c752022-01-26 12:19:54 -08001550 timestamp =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001551 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001552 }
1553 else if (propertyMap.first == "UpdateTimestamp")
1554 {
Ed Tanousc419c752022-01-26 12:19:54 -08001555 updateTimestamp =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001556 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001557 }
1558 else if (propertyMap.first == "Severity")
1559 {
1560 severity = std::get_if<std::string>(
1561 &propertyMap.second);
1562 }
1563 else if (propertyMap.first == "Message")
1564 {
1565 message = std::get_if<std::string>(
1566 &propertyMap.second);
1567 }
1568 else if (propertyMap.first == "Resolved")
1569 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001570 const bool* resolveptr =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001571 std::get_if<bool>(&propertyMap.second);
1572 if (resolveptr == nullptr)
1573 {
1574 messages::internalError(asyncResp->res);
1575 return;
1576 }
1577 resolved = *resolveptr;
1578 }
1579 else if (propertyMap.first == "Path")
1580 {
1581 filePath = std::get_if<std::string>(
1582 &propertyMap.second);
1583 }
1584 }
1585 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001586 severity == nullptr || timestamp == nullptr ||
1587 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001588 {
1589 messages::internalError(asyncResp->res);
1590 return;
1591 }
1592 asyncResp->res.jsonValue["@odata.type"] =
1593 "#LogEntry.v1_8_0.LogEntry";
1594 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001595 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001596 std::to_string(*id);
1597 asyncResp->res.jsonValue["Name"] =
1598 "System Event Log Entry";
1599 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1600 asyncResp->res.jsonValue["Message"] = *message;
1601 asyncResp->res.jsonValue["Resolved"] = resolved;
1602 asyncResp->res.jsonValue["EntryType"] = "Event";
1603 asyncResp->res.jsonValue["Severity"] =
1604 translateSeverityDbusToRedfish(*severity);
1605 asyncResp->res.jsonValue["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001606 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001607 asyncResp->res.jsonValue["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001608 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001609 if (filePath != nullptr)
1610 {
1611 asyncResp->res.jsonValue["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001612 "/redfish/v1/Systems/system/LogServices/EventLog/attachment/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001613 std::to_string(*id);
1614 }
1615 },
1616 "xyz.openbmc_project.Logging",
1617 "/xyz/openbmc_project/logging/entry/" + entryID,
1618 "org.freedesktop.DBus.Properties", "GetAll", "");
1619 });
1620
1621 BMCWEB_ROUTE(
1622 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001623 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001624 .methods(boost::beast::http::verb::patch)(
1625 [](const crow::Request& req,
1626 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1627 const std::string& entryId) {
1628 std::optional<bool> resolved;
1629
1630 if (!json_util::readJson(req, asyncResp->res, "Resolved",
1631 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001632 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001633 return;
1634 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001635 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001636
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001637 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001638 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001639 if (ec)
1640 {
1641 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1642 messages::internalError(asyncResp->res);
1643 return;
1644 }
1645 },
1646 "xyz.openbmc_project.Logging",
1647 "/xyz/openbmc_project/logging/entry/" + entryId,
1648 "org.freedesktop.DBus.Properties", "Set",
1649 "xyz.openbmc_project.Logging.Entry", "Resolved",
Ed Tanous168e20c2021-12-13 14:39:53 -08001650 dbus::utility::DbusVariantType(*resolved));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001651 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001652
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001653 BMCWEB_ROUTE(
1654 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001655 .privileges(redfish::privileges::deleteLogEntry)
1656
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001657 .methods(boost::beast::http::verb::delete_)(
1658 [](const crow::Request&,
1659 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1660 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001661
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001662 {
1663 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001664
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001665 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001666
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001667 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001668
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001669 // Process response from Logging service.
1670 auto respHandler = [asyncResp, entryID](
1671 const boost::system::error_code ec) {
1672 BMCWEB_LOG_DEBUG
1673 << "EventLogEntry (DBus) doDelete callback: Done";
1674 if (ec)
1675 {
1676 if (ec.value() == EBADR)
1677 {
1678 messages::resourceNotFound(asyncResp->res,
1679 "LogEntry", entryID);
1680 return;
1681 }
1682 // TODO Handle for specific error code
George Liu0fda0f12021-11-16 10:06:17 +08001683 BMCWEB_LOG_ERROR
1684 << "EventLogEntry (DBus) doDelete respHandler got error "
1685 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001686 asyncResp->res.result(
1687 boost::beast::http::status::internal_server_error);
1688 return;
1689 }
1690
1691 asyncResp->res.result(boost::beast::http::status::ok);
1692 };
1693
1694 // Make call to Logging service to request Delete Log
1695 crow::connections::systemBus->async_method_call(
1696 respHandler, "xyz.openbmc_project.Logging",
1697 "/xyz/openbmc_project/logging/entry/" + entryID,
1698 "xyz.openbmc_project.Object.Delete", "Delete");
1699 });
1700}
1701
1702inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001703{
George Liu0fda0f12021-11-16 10:06:17 +08001704 BMCWEB_ROUTE(
1705 app,
1706 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001707 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001708 .methods(boost::beast::http::verb::get)(
1709 [](const crow::Request& req,
1710 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1711 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001712
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001713 {
George Liu647b3cd2021-07-05 12:43:56 +08001714 if (!http_helpers::isOctetAccepted(
1715 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001716 {
1717 asyncResp->res.result(
1718 boost::beast::http::status::bad_request);
1719 return;
1720 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001721
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001722 std::string entryID = param;
1723 dbus::utility::escapePathForDbus(entryID);
1724
1725 crow::connections::systemBus->async_method_call(
1726 [asyncResp,
1727 entryID](const boost::system::error_code ec,
1728 const sdbusplus::message::unix_fd& unixfd) {
1729 if (ec.value() == EBADR)
1730 {
1731 messages::resourceNotFound(
1732 asyncResp->res, "EventLogAttachment", entryID);
1733 return;
1734 }
1735 if (ec)
1736 {
1737 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1738 messages::internalError(asyncResp->res);
1739 return;
1740 }
1741
1742 int fd = -1;
1743 fd = dup(unixfd);
1744 if (fd == -1)
1745 {
1746 messages::internalError(asyncResp->res);
1747 return;
1748 }
1749
1750 long long int size = lseek(fd, 0, SEEK_END);
1751 if (size == -1)
1752 {
1753 messages::internalError(asyncResp->res);
1754 return;
1755 }
1756
1757 // Arbitrary max size of 64kb
1758 constexpr int maxFileSize = 65536;
1759 if (size > maxFileSize)
1760 {
1761 BMCWEB_LOG_ERROR
1762 << "File size exceeds maximum allowed size of "
1763 << maxFileSize;
1764 messages::internalError(asyncResp->res);
1765 return;
1766 }
1767 std::vector<char> data(static_cast<size_t>(size));
1768 long long int rc = lseek(fd, 0, SEEK_SET);
1769 if (rc == -1)
1770 {
1771 messages::internalError(asyncResp->res);
1772 return;
1773 }
1774 rc = read(fd, data.data(), data.size());
1775 if ((rc == -1) || (rc != size))
1776 {
1777 messages::internalError(asyncResp->res);
1778 return;
1779 }
1780 close(fd);
1781
1782 std::string_view strData(data.data(), data.size());
1783 std::string output =
1784 crow::utility::base64encode(strData);
1785
1786 asyncResp->res.addHeader("Content-Type",
1787 "application/octet-stream");
1788 asyncResp->res.addHeader("Content-Transfer-Encoding",
1789 "Base64");
1790 asyncResp->res.body() = std::move(output);
1791 },
1792 "xyz.openbmc_project.Logging",
1793 "/xyz/openbmc_project/logging/entry/" + entryID,
1794 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1795 });
1796}
1797
Spencer Kub7028eb2021-10-26 15:27:35 +08001798constexpr const char* hostLoggerFolderPath = "/var/log/console";
1799
1800inline bool
1801 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1802 std::vector<std::filesystem::path>& hostLoggerFiles)
1803{
1804 std::error_code ec;
1805 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1806 if (ec)
1807 {
1808 BMCWEB_LOG_ERROR << ec.message();
1809 return false;
1810 }
1811 for (const std::filesystem::directory_entry& it : logPath)
1812 {
1813 std::string filename = it.path().filename();
1814 // Prefix of each log files is "log". Find the file and save the
1815 // path
1816 if (boost::starts_with(filename, "log"))
1817 {
1818 hostLoggerFiles.emplace_back(it.path());
1819 }
1820 }
1821 // As the log files rotate, they are appended with a ".#" that is higher for
1822 // the older logs. Since we start from oldest logs, sort the name in
1823 // descending order.
1824 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1825 AlphanumLess<std::string>());
1826
1827 return true;
1828}
1829
1830inline bool
1831 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
1832 uint64_t& skip, uint64_t& top,
1833 std::vector<std::string>& logEntries, size_t& logCount)
1834{
1835 GzFileReader logFile;
1836
1837 // Go though all log files and expose host logs.
1838 for (const std::filesystem::path& it : hostLoggerFiles)
1839 {
1840 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1841 {
1842 BMCWEB_LOG_ERROR << "fail to expose host logs";
1843 return false;
1844 }
1845 }
1846 // Get lastMessage from constructor by getter
1847 std::string lastMessage = logFile.getLastMessage();
1848 if (!lastMessage.empty())
1849 {
1850 logCount++;
1851 if (logCount > skip && logCount <= (skip + top))
1852 {
1853 logEntries.push_back(lastMessage);
1854 }
1855 }
1856 return true;
1857}
1858
1859inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1860 const std::string& msg,
1861 nlohmann::json& logEntryJson)
1862{
1863 // Fill in the log entry with the gathered data.
1864 logEntryJson = {
1865 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1866 {"@odata.id",
1867 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1868 logEntryID},
1869 {"Name", "Host Logger Entry"},
1870 {"Id", logEntryID},
1871 {"Message", msg},
1872 {"EntryType", "Oem"},
1873 {"Severity", "OK"},
1874 {"OemRecordFormat", "Host Logger Entry"}};
1875}
1876
1877inline void requestRoutesSystemHostLogger(App& app)
1878{
1879 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1880 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08001881 .methods(
1882 boost::beast::http::verb::
1883 get)([](const crow::Request&,
1884 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1885 asyncResp->res.jsonValue["@odata.id"] =
1886 "/redfish/v1/Systems/system/LogServices/HostLogger";
1887 asyncResp->res.jsonValue["@odata.type"] =
1888 "#LogService.v1_1_0.LogService";
1889 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1890 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1891 asyncResp->res.jsonValue["Id"] = "HostLogger";
1892 asyncResp->res.jsonValue["Entries"] = {
1893 {"@odata.id",
1894 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries"}};
1895 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001896}
1897
1898inline void requestRoutesSystemHostLoggerCollection(App& app)
1899{
1900 BMCWEB_ROUTE(app,
1901 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1902 .privileges(redfish::privileges::getLogEntry)
George Liu0fda0f12021-11-16 10:06:17 +08001903 .methods(
1904 boost::beast::http::verb::
1905 get)([](const crow::Request& req,
1906 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1907 uint64_t skip = 0;
1908 uint64_t top = maxEntriesPerPage; // Show max 1000 entries by
1909 // default, allow range 1 to
1910 // 1000 entries per page.
1911 if (!getSkipParam(asyncResp, req, skip))
1912 {
1913 return;
1914 }
1915 if (!getTopParam(asyncResp, req, top))
1916 {
1917 return;
1918 }
1919 asyncResp->res.jsonValue["@odata.id"] =
1920 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1921 asyncResp->res.jsonValue["@odata.type"] =
1922 "#LogEntryCollection.LogEntryCollection";
1923 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1924 asyncResp->res.jsonValue["Description"] =
1925 "Collection of HostLogger Entries";
1926 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1927 logEntryArray = nlohmann::json::array();
1928 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001929
George Liu0fda0f12021-11-16 10:06:17 +08001930 std::vector<std::filesystem::path> hostLoggerFiles;
1931 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1932 {
1933 BMCWEB_LOG_ERROR << "fail to get host log file path";
1934 return;
1935 }
1936
1937 size_t logCount = 0;
1938 // This vector only store the entries we want to expose that
1939 // control by skip and top.
1940 std::vector<std::string> logEntries;
1941 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1942 logCount))
1943 {
1944 messages::internalError(asyncResp->res);
1945 return;
1946 }
1947 // If vector is empty, that means skip value larger than total
1948 // log count
Ed Tanous26f69762022-01-25 09:49:11 -08001949 if (logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001950 {
1951 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1952 return;
1953 }
Ed Tanous26f69762022-01-25 09:49:11 -08001954 if (!logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001955 {
1956 for (size_t i = 0; i < logEntries.size(); i++)
Spencer Kub7028eb2021-10-26 15:27:35 +08001957 {
George Liu0fda0f12021-11-16 10:06:17 +08001958 logEntryArray.push_back({});
1959 nlohmann::json& hostLogEntry = logEntryArray.back();
1960 fillHostLoggerEntryJson(std::to_string(skip + i),
1961 logEntries[i], hostLogEntry);
Spencer Kub7028eb2021-10-26 15:27:35 +08001962 }
1963
George Liu0fda0f12021-11-16 10:06:17 +08001964 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1965 if (skip + top < logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001966 {
George Liu0fda0f12021-11-16 10:06:17 +08001967 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1968 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
1969 std::to_string(skip + top);
Spencer Kub7028eb2021-10-26 15:27:35 +08001970 }
George Liu0fda0f12021-11-16 10:06:17 +08001971 }
1972 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001973}
1974
1975inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1976{
1977 BMCWEB_ROUTE(
1978 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1979 .privileges(redfish::privileges::getLogEntry)
1980 .methods(boost::beast::http::verb::get)(
1981 [](const crow::Request&,
1982 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1983 const std::string& param) {
1984 const std::string& targetID = param;
1985
1986 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001987
1988 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1989 const char* end = targetID.data() + targetID.size();
1990
1991 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
Spencer Kub7028eb2021-10-26 15:27:35 +08001992 if (ec == std::errc::invalid_argument)
1993 {
1994 messages::resourceMissingAtURI(asyncResp->res, targetID);
1995 return;
1996 }
1997 if (ec == std::errc::result_out_of_range)
1998 {
1999 messages::resourceMissingAtURI(asyncResp->res, targetID);
2000 return;
2001 }
2002
2003 std::vector<std::filesystem::path> hostLoggerFiles;
2004 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2005 {
2006 BMCWEB_LOG_ERROR << "fail to get host log file path";
2007 return;
2008 }
2009
2010 size_t logCount = 0;
2011 uint64_t top = 1;
2012 std::vector<std::string> logEntries;
2013 // We can get specific entry by skip and top. For example, if we
2014 // want to get nth entry, we can set skip = n-1 and top = 1 to
2015 // get that entry
2016 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
2017 logEntries, logCount))
2018 {
2019 messages::internalError(asyncResp->res);
2020 return;
2021 }
2022
2023 if (!logEntries.empty())
2024 {
2025 fillHostLoggerEntryJson(targetID, logEntries[0],
2026 asyncResp->res.jsonValue);
2027 return;
2028 }
2029
2030 // Requested ID was not found
2031 messages::resourceMissingAtURI(asyncResp->res, targetID);
2032 });
2033}
2034
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002035inline void requestRoutesBMCLogServiceCollection(App& app)
2036{
2037 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002038 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002039 .methods(boost::beast::http::verb::get)(
2040 [](const crow::Request&,
2041 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2042 // Collections don't include the static data added by SubRoute
2043 // because it has a duplicate entry for members
2044 asyncResp->res.jsonValue["@odata.type"] =
2045 "#LogServiceCollection.LogServiceCollection";
2046 asyncResp->res.jsonValue["@odata.id"] =
2047 "/redfish/v1/Managers/bmc/LogServices";
2048 asyncResp->res.jsonValue["Name"] =
2049 "Open BMC Log Services Collection";
2050 asyncResp->res.jsonValue["Description"] =
2051 "Collection of LogServices for this Manager";
2052 nlohmann::json& logServiceArray =
2053 asyncResp->res.jsonValue["Members"];
2054 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002055#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002056 logServiceArray.push_back(
2057 {{"@odata.id",
2058 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002059#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002060#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002061 logServiceArray.push_back(
2062 {{"@odata.id",
2063 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002064#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002065 asyncResp->res.jsonValue["Members@odata.count"] =
2066 logServiceArray.size();
2067 });
2068}
Ed Tanous1da66f72018-07-27 16:13:37 -07002069
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002070inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002071{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002072 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002073 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002074 .methods(boost::beast::http::verb::get)(
2075 [](const crow::Request&,
2076 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002077
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002078 {
2079 asyncResp->res.jsonValue["@odata.type"] =
2080 "#LogService.v1_1_0.LogService";
2081 asyncResp->res.jsonValue["@odata.id"] =
2082 "/redfish/v1/Managers/bmc/LogServices/Journal";
2083 asyncResp->res.jsonValue["Name"] =
2084 "Open BMC Journal Log Service";
2085 asyncResp->res.jsonValue["Description"] =
2086 "BMC Journal Log Service";
2087 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2088 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302089
2090 std::pair<std::string, std::string> redfishDateTimeOffset =
2091 crow::utility::getDateTimeOffsetNow();
2092 asyncResp->res.jsonValue["DateTime"] =
2093 redfishDateTimeOffset.first;
2094 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2095 redfishDateTimeOffset.second;
2096
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002097 asyncResp->res.jsonValue["Entries"] = {
2098 {"@odata.id",
2099 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
2100 });
2101}
Jason M. Billse1f26342018-07-18 12:12:00 -07002102
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002103static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2104 sd_journal* journal,
2105 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002106{
2107 // Get the Log Entry contents
2108 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002109
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002110 std::string message;
2111 std::string_view syslogID;
2112 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2113 if (ret < 0)
2114 {
2115 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2116 << strerror(-ret);
2117 }
2118 if (!syslogID.empty())
2119 {
2120 message += std::string(syslogID) + ": ";
2121 }
2122
Ed Tanous39e77502019-03-04 17:35:53 -08002123 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002124 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002125 if (ret < 0)
2126 {
2127 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2128 return 1;
2129 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002130 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002131
2132 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002133 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002134 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002135 if (ret < 0)
2136 {
2137 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002138 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002139
2140 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002141 std::string entryTimeStr;
2142 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002143 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002144 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002145 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002146
2147 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002148 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002149 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002150 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2151 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002152 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002153 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002154 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002155 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002156 {"Severity", severity <= 2 ? "Critical"
2157 : severity <= 4 ? "Warning"
2158 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002159 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002160 {"Created", std::move(entryTimeStr)}};
2161 return 0;
2162}
2163
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002164inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002165{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002166 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002167 .privileges(redfish::privileges::getLogEntryCollection)
George Liu0fda0f12021-11-16 10:06:17 +08002168 .methods(
2169 boost::beast::http::verb::
2170 get)([](const crow::Request& req,
2171 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2172 static constexpr const long maxEntriesPerPage = 1000;
2173 uint64_t skip = 0;
2174 uint64_t top = maxEntriesPerPage; // Show max entries by default
2175 if (!getSkipParam(asyncResp, req, skip))
2176 {
2177 return;
2178 }
2179 if (!getTopParam(asyncResp, req, top))
2180 {
2181 return;
2182 }
2183 // Collections don't include the static data added by SubRoute
2184 // because it has a duplicate entry for members
2185 asyncResp->res.jsonValue["@odata.type"] =
2186 "#LogEntryCollection.LogEntryCollection";
2187 asyncResp->res.jsonValue["@odata.id"] =
2188 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2189 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2190 asyncResp->res.jsonValue["Description"] =
2191 "Collection of BMC Journal Entries";
2192 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2193 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002194
George Liu0fda0f12021-11-16 10:06:17 +08002195 // Go through the journal and use the timestamp to create a
2196 // unique ID for each entry
2197 sd_journal* journalTmp = nullptr;
2198 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2199 if (ret < 0)
2200 {
2201 BMCWEB_LOG_ERROR << "failed to open journal: "
2202 << strerror(-ret);
2203 messages::internalError(asyncResp->res);
2204 return;
2205 }
2206 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2207 journalTmp, sd_journal_close);
2208 journalTmp = nullptr;
2209 uint64_t entryCount = 0;
2210 // Reset the unique ID on the first entry
2211 bool firstEntry = true;
2212 SD_JOURNAL_FOREACH(journal.get())
2213 {
2214 entryCount++;
2215 // Handle paging using skip (number of entries to skip from
2216 // the start) and top (number of entries to display)
2217 if (entryCount <= skip || entryCount > skip + top)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002218 {
George Liu0fda0f12021-11-16 10:06:17 +08002219 continue;
2220 }
2221
2222 std::string idStr;
2223 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2224 {
2225 continue;
2226 }
2227
2228 if (firstEntry)
2229 {
2230 firstEntry = false;
2231 }
2232
2233 logEntryArray.push_back({});
2234 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2235 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2236 bmcJournalLogEntry) != 0)
2237 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002238 messages::internalError(asyncResp->res);
2239 return;
2240 }
George Liu0fda0f12021-11-16 10:06:17 +08002241 }
2242 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2243 if (skip + top < entryCount)
2244 {
2245 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2246 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2247 std::to_string(skip + top);
2248 }
2249 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002250}
Jason M. Billse1f26342018-07-18 12:12:00 -07002251
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002252inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002253{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002254 BMCWEB_ROUTE(app,
2255 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002256 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002257 .methods(boost::beast::http::verb::get)(
2258 [](const crow::Request&,
2259 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2260 const std::string& entryID) {
2261 // Convert the unique ID back to a timestamp to find the entry
2262 uint64_t ts = 0;
2263 uint64_t index = 0;
2264 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2265 {
2266 return;
2267 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002268
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002269 sd_journal* journalTmp = nullptr;
2270 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2271 if (ret < 0)
2272 {
2273 BMCWEB_LOG_ERROR << "failed to open journal: "
2274 << strerror(-ret);
2275 messages::internalError(asyncResp->res);
2276 return;
2277 }
2278 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2279 journal(journalTmp, sd_journal_close);
2280 journalTmp = nullptr;
2281 // Go to the timestamp in the log and move to the entry at the
2282 // index tracking the unique ID
2283 std::string idStr;
2284 bool firstEntry = true;
2285 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2286 if (ret < 0)
2287 {
2288 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2289 << strerror(-ret);
2290 messages::internalError(asyncResp->res);
2291 return;
2292 }
2293 for (uint64_t i = 0; i <= index; i++)
2294 {
2295 sd_journal_next(journal.get());
2296 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2297 {
2298 messages::internalError(asyncResp->res);
2299 return;
2300 }
2301 if (firstEntry)
2302 {
2303 firstEntry = false;
2304 }
2305 }
2306 // Confirm that the entry ID matches what was requested
2307 if (idStr != entryID)
2308 {
2309 messages::resourceMissingAtURI(asyncResp->res, entryID);
2310 return;
2311 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002312
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002313 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2314 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002315 {
2316 messages::internalError(asyncResp->res);
2317 return;
2318 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002319 });
2320}
2321
2322inline void requestRoutesBMCDumpService(App& app)
2323{
2324 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002325 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08002326 .methods(
2327 boost::beast::http::verb::
2328 get)([](const crow::Request&,
2329 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2330 asyncResp->res.jsonValue["@odata.id"] =
2331 "/redfish/v1/Managers/bmc/LogServices/Dump";
2332 asyncResp->res.jsonValue["@odata.type"] =
2333 "#LogService.v1_2_0.LogService";
2334 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2335 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2336 asyncResp->res.jsonValue["Id"] = "Dump";
2337 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302338
George Liu0fda0f12021-11-16 10:06:17 +08002339 std::pair<std::string, std::string> redfishDateTimeOffset =
2340 crow::utility::getDateTimeOffsetNow();
2341 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2342 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2343 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302344
George Liu0fda0f12021-11-16 10:06:17 +08002345 asyncResp->res.jsonValue["Entries"] = {
2346 {"@odata.id",
2347 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2348 asyncResp->res.jsonValue["Actions"] = {
2349 {"#LogService.ClearLog",
2350 {{"target",
2351 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog"}}},
2352 {"#LogService.CollectDiagnosticData",
2353 {{"target",
2354 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
2355 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002356}
2357
2358inline void requestRoutesBMCDumpEntryCollection(App& app)
2359{
2360
2361 /**
2362 * Functions triggers appropriate requests on DBus
2363 */
2364 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002365 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002366 .methods(boost::beast::http::verb::get)(
2367 [](const crow::Request&,
2368 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2369 asyncResp->res.jsonValue["@odata.type"] =
2370 "#LogEntryCollection.LogEntryCollection";
2371 asyncResp->res.jsonValue["@odata.id"] =
2372 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2373 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2374 asyncResp->res.jsonValue["Description"] =
2375 "Collection of BMC Dump Entries";
2376
2377 getDumpEntryCollection(asyncResp, "BMC");
2378 });
2379}
2380
2381inline void requestRoutesBMCDumpEntry(App& app)
2382{
2383 BMCWEB_ROUTE(app,
2384 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002385 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002386 .methods(boost::beast::http::verb::get)(
2387 [](const crow::Request&,
2388 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2389 const std::string& param) {
2390 getDumpEntryById(asyncResp, param, "BMC");
2391 });
2392 BMCWEB_ROUTE(app,
2393 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002394 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002395 .methods(boost::beast::http::verb::delete_)(
2396 [](const crow::Request&,
2397 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2398 const std::string& param) {
2399 deleteDumpEntry(asyncResp, param, "bmc");
2400 });
2401}
2402
2403inline void requestRoutesBMCDumpCreate(App& app)
2404{
2405
George Liu0fda0f12021-11-16 10:06:17 +08002406 BMCWEB_ROUTE(
2407 app,
2408 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002409 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002410 .methods(boost::beast::http::verb::post)(
2411 [](const crow::Request& req,
2412 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2413 createDump(asyncResp, req, "BMC");
2414 });
2415}
2416
2417inline void requestRoutesBMCDumpClear(App& app)
2418{
George Liu0fda0f12021-11-16 10:06:17 +08002419 BMCWEB_ROUTE(
2420 app,
2421 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002422 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002423 .methods(boost::beast::http::verb::post)(
2424 [](const crow::Request&,
2425 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2426 clearDump(asyncResp, "BMC");
2427 });
2428}
2429
2430inline void requestRoutesSystemDumpService(App& app)
2431{
2432 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002433 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002434 .methods(boost::beast::http::verb::get)(
2435 [](const crow::Request&,
2436 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2437
2438 {
2439 asyncResp->res.jsonValue["@odata.id"] =
2440 "/redfish/v1/Systems/system/LogServices/Dump";
2441 asyncResp->res.jsonValue["@odata.type"] =
2442 "#LogService.v1_2_0.LogService";
2443 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2444 asyncResp->res.jsonValue["Description"] =
2445 "System Dump LogService";
2446 asyncResp->res.jsonValue["Id"] = "Dump";
2447 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302448
2449 std::pair<std::string, std::string> redfishDateTimeOffset =
2450 crow::utility::getDateTimeOffsetNow();
2451 asyncResp->res.jsonValue["DateTime"] =
2452 redfishDateTimeOffset.first;
2453 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2454 redfishDateTimeOffset.second;
2455
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002456 asyncResp->res.jsonValue["Entries"] = {
2457 {"@odata.id",
2458 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2459 asyncResp->res.jsonValue["Actions"] = {
2460 {"#LogService.ClearLog",
2461 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002462 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002463 {"#LogService.CollectDiagnosticData",
2464 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002465 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002466 });
2467}
2468
2469inline void requestRoutesSystemDumpEntryCollection(App& app)
2470{
2471
2472 /**
2473 * Functions triggers appropriate requests on DBus
2474 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002475 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002476 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002477 .methods(boost::beast::http::verb::get)(
2478 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002479 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002480 asyncResp->res.jsonValue["@odata.type"] =
2481 "#LogEntryCollection.LogEntryCollection";
2482 asyncResp->res.jsonValue["@odata.id"] =
2483 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2484 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2485 asyncResp->res.jsonValue["Description"] =
2486 "Collection of System Dump Entries";
2487
2488 getDumpEntryCollection(asyncResp, "System");
2489 });
2490}
2491
2492inline void requestRoutesSystemDumpEntry(App& app)
2493{
2494 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002495 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002496 .privileges(redfish::privileges::getLogEntry)
2497
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002498 .methods(boost::beast::http::verb::get)(
2499 [](const crow::Request&,
2500 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2501 const std::string& param) {
2502 getDumpEntryById(asyncResp, param, "System");
2503 });
2504
2505 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002506 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002507 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002508 .methods(boost::beast::http::verb::delete_)(
2509 [](const crow::Request&,
2510 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2511 const std::string& param) {
2512 deleteDumpEntry(asyncResp, param, "system");
2513 });
2514}
2515
2516inline void requestRoutesSystemDumpCreate(App& app)
2517{
George Liu0fda0f12021-11-16 10:06:17 +08002518 BMCWEB_ROUTE(
2519 app,
2520 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002521 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002522 .methods(boost::beast::http::verb::post)(
2523 [](const crow::Request& req,
2524 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2525
2526 { createDump(asyncResp, req, "System"); });
2527}
2528
2529inline void requestRoutesSystemDumpClear(App& app)
2530{
George Liu0fda0f12021-11-16 10:06:17 +08002531 BMCWEB_ROUTE(
2532 app,
2533 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002534 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002535 .methods(boost::beast::http::verb::post)(
2536 [](const crow::Request&,
2537 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2538
2539 { clearDump(asyncResp, "System"); });
2540}
2541
2542inline void requestRoutesCrashdumpService(App& app)
2543{
2544 // Note: Deviated from redfish privilege registry for GET & HEAD
2545 // method for security reasons.
2546 /**
2547 * Functions triggers appropriate requests on DBus
2548 */
2549 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002550 // This is incorrect, should be:
2551 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002552 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002553 .methods(
2554 boost::beast::http::verb::
2555 get)([](const crow::Request&,
2556 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2557 // Copy over the static data to include the entries added by
2558 // SubRoute
2559 asyncResp->res.jsonValue["@odata.id"] =
2560 "/redfish/v1/Systems/system/LogServices/Crashdump";
2561 asyncResp->res.jsonValue["@odata.type"] =
2562 "#LogService.v1_2_0.LogService";
2563 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2564 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2565 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2566 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2567 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302568
2569 std::pair<std::string, std::string> redfishDateTimeOffset =
2570 crow::utility::getDateTimeOffsetNow();
2571 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2572 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2573 redfishDateTimeOffset.second;
2574
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002575 asyncResp->res.jsonValue["Entries"] = {
2576 {"@odata.id",
2577 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2578 asyncResp->res.jsonValue["Actions"] = {
2579 {"#LogService.ClearLog",
George Liu0fda0f12021-11-16 10:06:17 +08002580 {{"target",
2581 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002582 {"#LogService.CollectDiagnosticData",
George Liu0fda0f12021-11-16 10:06:17 +08002583 {{"target",
2584 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002585 });
2586}
2587
2588void inline requestRoutesCrashdumpClear(App& app)
2589{
George Liu0fda0f12021-11-16 10:06:17 +08002590 BMCWEB_ROUTE(
2591 app,
2592 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002593 // This is incorrect, should be:
2594 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002595 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002596 .methods(boost::beast::http::verb::post)(
2597 [](const crow::Request&,
2598 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2599 crow::connections::systemBus->async_method_call(
2600 [asyncResp](const boost::system::error_code ec,
2601 const std::string&) {
2602 if (ec)
2603 {
2604 messages::internalError(asyncResp->res);
2605 return;
2606 }
2607 messages::success(asyncResp->res);
2608 },
2609 crashdumpObject, crashdumpPath, deleteAllInterface,
2610 "DeleteAll");
2611 });
2612}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002613
zhanghch058d1b46d2021-04-01 11:18:24 +08002614static void
2615 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2616 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002617{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002618 auto getStoredLogCallback =
2619 [asyncResp, logID, &logEntryJson](
2620 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002621 const std::vector<
2622 std::pair<std::string, dbus::utility::DbusVariantType>>&
2623 params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002624 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002625 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002626 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2627 if (ec.value() ==
2628 boost::system::linux_error::bad_request_descriptor)
2629 {
2630 messages::resourceNotFound(asyncResp->res, "LogEntry",
2631 logID);
2632 }
2633 else
2634 {
2635 messages::internalError(asyncResp->res);
2636 }
2637 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002638 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002639
Johnathan Mantey043a0532020-03-10 17:15:28 -07002640 std::string timestamp{};
2641 std::string filename{};
2642 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002643 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002644
2645 if (filename.empty() || timestamp.empty())
2646 {
2647 messages::resourceMissingAtURI(asyncResp->res, logID);
2648 return;
2649 }
2650
2651 std::string crashdumpURI =
2652 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2653 logID + "/" + filename;
Ed Tanousd0dbeef2021-07-01 08:46:46 -07002654 logEntryJson = {{"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002655 {"@odata.id", "/redfish/v1/Systems/system/"
2656 "LogServices/Crashdump/Entries/" +
2657 logID},
2658 {"Name", "CPU Crashdump"},
2659 {"Id", logID},
2660 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002661 {"AdditionalDataURI", std::move(crashdumpURI)},
2662 {"DiagnosticDataType", "OEM"},
2663 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002664 {"Created", std::move(timestamp)}};
2665 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002666 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002667 std::move(getStoredLogCallback), crashdumpObject,
2668 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002669 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002670}
2671
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002672inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002673{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002674 // Note: Deviated from redfish privilege registry for GET & HEAD
2675 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002676 /**
2677 * Functions triggers appropriate requests on DBus
2678 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002679 BMCWEB_ROUTE(app,
2680 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002681 // This is incorrect, should be.
2682 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002683 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002684 .methods(
2685 boost::beast::http::verb::
2686 get)([](const crow::Request&,
2687 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2688 // Collections don't include the static data added by SubRoute
2689 // because it has a duplicate entry for members
2690 auto getLogEntriesCallback = [asyncResp](
2691 const boost::system::error_code ec,
2692 const std::vector<std::string>&
2693 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002694 if (ec)
2695 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002696 if (ec.value() !=
2697 boost::system::errc::no_such_file_or_directory)
2698 {
2699 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2700 << ec.message();
2701 messages::internalError(asyncResp->res);
2702 return;
2703 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002704 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002705 asyncResp->res.jsonValue["@odata.type"] =
2706 "#LogEntryCollection.LogEntryCollection";
2707 asyncResp->res.jsonValue["@odata.id"] =
2708 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2709 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2710 asyncResp->res.jsonValue["Description"] =
2711 "Collection of Crashdump Entries";
2712 nlohmann::json& logEntryArray =
2713 asyncResp->res.jsonValue["Members"];
2714 logEntryArray = nlohmann::json::array();
2715 std::vector<std::string> logIDs;
2716 // Get the list of log entries and build up an empty array big
2717 // enough to hold them
2718 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002719 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002720 // Get the log ID
2721 std::size_t lastPos = objpath.rfind('/');
2722 if (lastPos == std::string::npos)
2723 {
2724 continue;
2725 }
2726 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002727
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002728 // Add a space for the log entry to the array
2729 logEntryArray.push_back({});
2730 }
2731 // Now go through and set up async calls to fill in the entries
2732 size_t index = 0;
2733 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002734 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002735 // Add the log entry to the array
2736 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002737 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002738 asyncResp->res.jsonValue["Members@odata.count"] =
2739 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002740 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002741 crow::connections::systemBus->async_method_call(
2742 std::move(getLogEntriesCallback),
2743 "xyz.openbmc_project.ObjectMapper",
2744 "/xyz/openbmc_project/object_mapper",
2745 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2746 std::array<const char*, 1>{crashdumpInterface});
2747 });
2748}
Ed Tanous1da66f72018-07-27 16:13:37 -07002749
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002750inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002751{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002752 // Note: Deviated from redfish privilege registry for GET & HEAD
2753 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002754
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002755 BMCWEB_ROUTE(
2756 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002757 // this is incorrect, should be
2758 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002759 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002760 .methods(boost::beast::http::verb::get)(
2761 [](const crow::Request&,
2762 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2763 const std::string& param) {
2764 const std::string& logID = param;
2765 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2766 });
2767}
Ed Tanous1da66f72018-07-27 16:13:37 -07002768
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002769inline void requestRoutesCrashdumpFile(App& app)
2770{
2771 // Note: Deviated from redfish privilege registry for GET & HEAD
2772 // method for security reasons.
2773 BMCWEB_ROUTE(
2774 app,
2775 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002776 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002777 .methods(boost::beast::http::verb::get)(
2778 [](const crow::Request&,
2779 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2780 const std::string& logID, const std::string& fileName) {
2781 auto getStoredLogCallback =
2782 [asyncResp, logID, fileName](
2783 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002784 const std::vector<std::pair<
2785 std::string, dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002786 resp) {
2787 if (ec)
2788 {
2789 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2790 << ec.message();
2791 messages::internalError(asyncResp->res);
2792 return;
2793 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002794
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002795 std::string dbusFilename{};
2796 std::string dbusTimestamp{};
2797 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002798
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002799 parseCrashdumpParameters(resp, dbusFilename,
2800 dbusTimestamp, dbusFilepath);
2801
2802 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2803 dbusFilepath.empty())
2804 {
2805 messages::resourceMissingAtURI(asyncResp->res,
2806 fileName);
2807 return;
2808 }
2809
2810 // Verify the file name parameter is correct
2811 if (fileName != dbusFilename)
2812 {
2813 messages::resourceMissingAtURI(asyncResp->res,
2814 fileName);
2815 return;
2816 }
2817
2818 if (!std::filesystem::exists(dbusFilepath))
2819 {
2820 messages::resourceMissingAtURI(asyncResp->res,
2821 fileName);
2822 return;
2823 }
Jason M. Bills2d314912022-01-12 13:59:01 -08002824 std::ifstream ifs(dbusFilepath,
2825 std::ios::in | std::ios::binary);
2826 asyncResp->res.body() = std::string(
2827 std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002828
2829 // Configure this to be a file download when accessed
2830 // from a browser
2831 asyncResp->res.addHeader("Content-Disposition",
2832 "attachment");
2833 };
2834 crow::connections::systemBus->async_method_call(
2835 std::move(getStoredLogCallback), crashdumpObject,
2836 crashdumpPath + std::string("/") + logID,
2837 "org.freedesktop.DBus.Properties", "GetAll",
2838 crashdumpInterface);
2839 });
2840}
2841
2842inline void requestRoutesCrashdumpCollect(App& app)
2843{
2844 // Note: Deviated from redfish privilege registry for GET & HEAD
2845 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002846 BMCWEB_ROUTE(
2847 app,
2848 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002849 // The below is incorrect; Should be ConfigureManager
2850 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002851 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002852 .methods(
2853 boost::beast::http::verb::
2854 post)([](const crow::Request& req,
2855 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2856 std::string diagnosticDataType;
2857 std::string oemDiagnosticDataType;
2858 if (!redfish::json_util::readJson(
2859 req, asyncResp->res, "DiagnosticDataType",
2860 diagnosticDataType, "OEMDiagnosticDataType",
2861 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002862 {
James Feist46229572020-02-19 15:11:58 -08002863 return;
2864 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002865
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002866 if (diagnosticDataType != "OEM")
2867 {
2868 BMCWEB_LOG_ERROR
2869 << "Only OEM DiagnosticDataType supported for Crashdump";
2870 messages::actionParameterValueFormatError(
2871 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2872 "CollectDiagnosticData");
2873 return;
2874 }
2875
Ed Tanous98be3e32021-09-16 15:05:36 -07002876 auto collectCrashdumpCallback = [asyncResp,
2877 payload(task::Payload(req))](
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002878 const boost::system::error_code
2879 ec,
Ed Tanous98be3e32021-09-16 15:05:36 -07002880 const std::string&) mutable {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002881 if (ec)
2882 {
2883 if (ec.value() ==
2884 boost::system::errc::operation_not_supported)
2885 {
2886 messages::resourceInStandby(asyncResp->res);
2887 }
2888 else if (ec.value() ==
2889 boost::system::errc::device_or_resource_busy)
2890 {
2891 messages::serviceTemporarilyUnavailable(asyncResp->res,
2892 "60");
2893 }
2894 else
2895 {
2896 messages::internalError(asyncResp->res);
2897 }
2898 return;
2899 }
George Liu0fda0f12021-11-16 10:06:17 +08002900 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2901 [](boost::system::error_code err,
2902 sdbusplus::message::message&,
2903 const std::shared_ptr<task::TaskData>& taskData) {
2904 if (!err)
2905 {
2906 taskData->messages.emplace_back(
2907 messages::taskCompletedOK(
2908 std::to_string(taskData->index)));
2909 taskData->state = "Completed";
2910 }
2911 return task::completed;
2912 },
2913 "type='signal',interface='org.freedesktop.DBus."
2914 "Properties',"
2915 "member='PropertiesChanged',arg0namespace='com.intel.crashdump'");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002916 task->startTimer(std::chrono::minutes(5));
2917 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -07002918 task->payload.emplace(std::move(payload));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002919 };
2920
2921 if (oemDiagnosticDataType == "OnDemand")
2922 {
2923 crow::connections::systemBus->async_method_call(
2924 std::move(collectCrashdumpCallback), crashdumpObject,
2925 crashdumpPath, crashdumpOnDemandInterface,
2926 "GenerateOnDemandLog");
2927 }
2928 else if (oemDiagnosticDataType == "Telemetry")
2929 {
2930 crow::connections::systemBus->async_method_call(
2931 std::move(collectCrashdumpCallback), crashdumpObject,
2932 crashdumpPath, crashdumpTelemetryInterface,
2933 "GenerateTelemetryLog");
2934 }
2935 else
2936 {
2937 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2938 << oemDiagnosticDataType;
2939 messages::actionParameterValueFormatError(
2940 asyncResp->res, oemDiagnosticDataType,
2941 "OEMDiagnosticDataType", "CollectDiagnosticData");
2942 return;
2943 }
2944 });
2945}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002946
Andrew Geisslercb92c032018-08-17 07:56:14 -07002947/**
2948 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2949 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002950inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002951{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002952 /**
2953 * Function handles POST method request.
2954 * The Clear Log actions does not require any parameter.The action deletes
2955 * all entries found in the Entries collection for this Log Service.
2956 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002957
George Liu0fda0f12021-11-16 10:06:17 +08002958 BMCWEB_ROUTE(
2959 app,
2960 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002961 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002962 .methods(boost::beast::http::verb::post)(
2963 [](const crow::Request&,
2964 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2965 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07002966
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002967 // Process response from Logging service.
2968 auto respHandler = [asyncResp](
2969 const boost::system::error_code ec) {
2970 BMCWEB_LOG_DEBUG
2971 << "doClearLog resp_handler callback: Done";
2972 if (ec)
2973 {
2974 // TODO Handle for specific error code
2975 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
2976 << ec;
2977 asyncResp->res.result(
2978 boost::beast::http::status::internal_server_error);
2979 return;
2980 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07002981
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002982 asyncResp->res.result(
2983 boost::beast::http::status::no_content);
2984 };
2985
2986 // Make call to Logging service to request Clear Log
2987 crow::connections::systemBus->async_method_call(
2988 respHandler, "xyz.openbmc_project.Logging",
2989 "/xyz/openbmc_project/logging",
2990 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2991 });
2992}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002993
2994/****************************************************
2995 * Redfish PostCode interfaces
2996 * using DBUS interface: getPostCodesTS
2997 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002998inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002999{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003000 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003001 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08003002 .methods(
3003 boost::beast::http::verb::
3004 get)([](const crow::Request&,
3005 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3006 asyncResp->res.jsonValue = {
3007 {"@odata.id",
3008 "/redfish/v1/Systems/system/LogServices/PostCodes"},
3009 {"@odata.type", "#LogService.v1_1_0.LogService"},
3010 {"Name", "POST Code Log Service"},
3011 {"Description", "POST Code Log Service"},
3012 {"Id", "BIOS POST Code Log"},
3013 {"OverWritePolicy", "WrapsWhenFull"},
3014 {"Entries",
3015 {{"@odata.id",
3016 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05303017
George Liu0fda0f12021-11-16 10:06:17 +08003018 std::pair<std::string, std::string> redfishDateTimeOffset =
3019 crow::utility::getDateTimeOffsetNow();
3020 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3021 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3022 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303023
George Liu0fda0f12021-11-16 10:06:17 +08003024 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3025 {"target",
3026 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3027 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003028}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003029
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003030inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003031{
George Liu0fda0f12021-11-16 10:06:17 +08003032 BMCWEB_ROUTE(
3033 app,
3034 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003035 // The following privilege is incorrect; It should be ConfigureManager
3036 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003037 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003038 .methods(boost::beast::http::verb::post)(
3039 [](const crow::Request&,
3040 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3041 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003042
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003043 // Make call to post-code service to request clear all
3044 crow::connections::systemBus->async_method_call(
3045 [asyncResp](const boost::system::error_code ec) {
3046 if (ec)
3047 {
3048 // TODO Handle for specific error code
3049 BMCWEB_LOG_ERROR
3050 << "doClearPostCodes resp_handler got error "
3051 << ec;
3052 asyncResp->res.result(boost::beast::http::status::
3053 internal_server_error);
3054 messages::internalError(asyncResp->res);
3055 return;
3056 }
3057 },
3058 "xyz.openbmc_project.State.Boot.PostCode0",
3059 "/xyz/openbmc_project/State/Boot/PostCode0",
3060 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3061 });
3062}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003063
3064static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003065 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303066 const boost::container::flat_map<
3067 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003068 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3069 const uint64_t skip = 0, const uint64_t top = 0)
3070{
3071 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003072 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05303073 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003074
3075 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003076 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003077
3078 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303079 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3080 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003081 {
3082 currentCodeIndex++;
3083 std::string postcodeEntryID =
3084 "B" + std::to_string(bootIndex) + "-" +
3085 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3086
3087 uint64_t usecSinceEpoch = code.first;
3088 uint64_t usTimeOffset = 0;
3089
3090 if (1 == currentCodeIndex)
3091 { // already incremented
3092 firstCodeTimeUs = code.first;
3093 }
3094 else
3095 {
3096 usTimeOffset = code.first - firstCodeTimeUs;
3097 }
3098
3099 // skip if no specific codeIndex is specified and currentCodeIndex does
3100 // not fall between top and skip
3101 if ((codeIndex == 0) &&
3102 (currentCodeIndex <= skip || currentCodeIndex > top))
3103 {
3104 continue;
3105 }
3106
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003107 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003108 // currentIndex
3109 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3110 {
3111 // This is done for simplicity. 1st entry is needed to calculate
3112 // time offset. To improve efficiency, one can get to the entry
3113 // directly (possibly with flatmap's nth method)
3114 continue;
3115 }
3116
3117 // currentCodeIndex is within top and skip or equal to specified code
3118 // index
3119
3120 // Get the Created time from the timestamp
3121 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003122 entryTimeStr =
3123 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003124
3125 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3126 std::ostringstream hexCode;
3127 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303128 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003129 std::ostringstream timeOffsetStr;
3130 // Set Fixed -Point Notation
3131 timeOffsetStr << std::fixed;
3132 // Set precision to 4 digits
3133 timeOffsetStr << std::setprecision(4);
3134 // Add double to stream
3135 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3136 std::vector<std::string> messageArgs = {
3137 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3138
3139 // Get MessageArgs template from message registry
3140 std::string msg;
3141 if (message != nullptr)
3142 {
3143 msg = message->message;
3144
3145 // fill in this post code value
3146 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003147 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003148 {
3149 std::string argStr = "%" + std::to_string(++i);
3150 size_t argPos = msg.find(argStr);
3151 if (argPos != std::string::npos)
3152 {
3153 msg.replace(argPos, argStr.length(), messageArg);
3154 }
3155 }
3156 }
3157
Tim Leed4342a92020-04-27 11:47:58 +08003158 // Get Severity template from message registry
3159 std::string severity;
3160 if (message != nullptr)
3161 {
3162 severity = message->severity;
3163 }
3164
ZhikuiRena3316fc2020-01-29 14:58:08 -08003165 // add to AsyncResp
3166 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003167 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003168 bmcLogEntry = {
3169 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3170 {"@odata.id",
3171 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3172 postcodeEntryID},
3173 {"Name", "POST Code Log Entry"},
3174 {"Id", postcodeEntryID},
3175 {"Message", std::move(msg)},
3176 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3177 {"MessageArgs", std::move(messageArgs)},
3178 {"EntryType", "Event"},
3179 {"Severity", std::move(severity)},
3180 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003181 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3182 {
3183 bmcLogEntry["AdditionalDataURI"] =
3184 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3185 postcodeEntryID + "/attachment";
3186 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003187 }
3188}
3189
zhanghch058d1b46d2021-04-01 11:18:24 +08003190static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003191 const uint16_t bootIndex,
3192 const uint64_t codeIndex)
3193{
3194 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303195 [aResp, bootIndex,
3196 codeIndex](const boost::system::error_code ec,
3197 const boost::container::flat_map<
3198 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3199 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003200 if (ec)
3201 {
3202 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3203 messages::internalError(aResp->res);
3204 return;
3205 }
3206
3207 // skip the empty postcode boots
3208 if (postcode.empty())
3209 {
3210 return;
3211 }
3212
3213 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3214
3215 aResp->res.jsonValue["Members@odata.count"] =
3216 aResp->res.jsonValue["Members"].size();
3217 },
Jonathan Doman15124762021-01-07 17:54:17 -08003218 "xyz.openbmc_project.State.Boot.PostCode0",
3219 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003220 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3221 bootIndex);
3222}
3223
zhanghch058d1b46d2021-04-01 11:18:24 +08003224static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003225 const uint16_t bootIndex,
3226 const uint16_t bootCount,
3227 const uint64_t entryCount, const uint64_t skip,
3228 const uint64_t top)
3229{
3230 crow::connections::systemBus->async_method_call(
3231 [aResp, bootIndex, bootCount, entryCount, skip,
3232 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303233 const boost::container::flat_map<
3234 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3235 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003236 if (ec)
3237 {
3238 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3239 messages::internalError(aResp->res);
3240 return;
3241 }
3242
3243 uint64_t endCount = entryCount;
3244 if (!postcode.empty())
3245 {
3246 endCount = entryCount + postcode.size();
3247
3248 if ((skip < endCount) && ((top + skip) > entryCount))
3249 {
3250 uint64_t thisBootSkip =
3251 std::max(skip, entryCount) - entryCount;
3252 uint64_t thisBootTop =
3253 std::min(top + skip, endCount) - entryCount;
3254
3255 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3256 thisBootSkip, thisBootTop);
3257 }
3258 aResp->res.jsonValue["Members@odata.count"] = endCount;
3259 }
3260
3261 // continue to previous bootIndex
3262 if (bootIndex < bootCount)
3263 {
3264 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3265 bootCount, endCount, skip, top);
3266 }
3267 else
3268 {
3269 aResp->res.jsonValue["Members@odata.nextLink"] =
George Liu0fda0f12021-11-16 10:06:17 +08003270 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
ZhikuiRena3316fc2020-01-29 14:58:08 -08003271 std::to_string(skip + top);
3272 }
3273 },
Jonathan Doman15124762021-01-07 17:54:17 -08003274 "xyz.openbmc_project.State.Boot.PostCode0",
3275 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003276 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3277 bootIndex);
3278}
3279
zhanghch058d1b46d2021-04-01 11:18:24 +08003280static void
3281 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3282 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003283{
3284 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003285 sdbusplus::asio::getProperty<uint16_t>(
3286 *crow::connections::systemBus,
3287 "xyz.openbmc_project.State.Boot.PostCode0",
3288 "/xyz/openbmc_project/State/Boot/PostCode0",
3289 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3290 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3291 const uint16_t bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003292 if (ec)
3293 {
3294 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3295 messages::internalError(aResp->res);
3296 return;
3297 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003298 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3299 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003300}
3301
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003302inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003303{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003304 BMCWEB_ROUTE(app,
3305 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003306 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003307 .methods(boost::beast::http::verb::get)(
3308 [](const crow::Request& req,
3309 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3310 asyncResp->res.jsonValue["@odata.type"] =
3311 "#LogEntryCollection.LogEntryCollection";
3312 asyncResp->res.jsonValue["@odata.id"] =
3313 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3314 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3315 asyncResp->res.jsonValue["Description"] =
3316 "Collection of POST Code Log Entries";
3317 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3318 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003319
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003320 uint64_t skip = 0;
3321 uint64_t top = maxEntriesPerPage; // Show max entries by default
3322 if (!getSkipParam(asyncResp, req, skip))
3323 {
3324 return;
3325 }
3326 if (!getTopParam(asyncResp, req, top))
3327 {
3328 return;
3329 }
3330 getCurrentBootNumber(asyncResp, skip, top);
3331 });
3332}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003333
George Liu647b3cd2021-07-05 12:43:56 +08003334/**
3335 * @brief Parse post code ID and get the current value and index value
3336 * eg: postCodeID=B1-2, currentValue=1, index=2
3337 *
3338 * @param[in] postCodeID Post Code ID
3339 * @param[out] currentValue Current value
3340 * @param[out] index Index value
3341 *
3342 * @return bool true if the parsing is successful, false the parsing fails
3343 */
3344inline static bool parsePostCode(const std::string& postCodeID,
3345 uint64_t& currentValue, uint16_t& index)
3346{
3347 std::vector<std::string> split;
3348 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3349 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3350 {
3351 return false;
3352 }
3353
Ed Tanousca45aa32022-01-07 09:28:45 -08003354 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003355 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003356 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003357 const char* end = split[0].data() + split[0].size();
3358 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3359
3360 if (ptrIndex != end || ecIndex != std::errc())
3361 {
3362 return false;
3363 }
3364
3365 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003366
3367 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003368 end = split[1].data() + split[1].size();
3369 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003370
Ed Tanousdcf2ebc2022-01-25 10:07:45 -08003371 return ptrValue == end && ecValue != std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003372}
3373
3374inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3375{
George Liu0fda0f12021-11-16 10:06:17 +08003376 BMCWEB_ROUTE(
3377 app,
3378 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003379 .privileges(redfish::privileges::getLogEntry)
3380 .methods(boost::beast::http::verb::get)(
3381 [](const crow::Request& req,
3382 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3383 const std::string& postCodeID) {
3384 if (!http_helpers::isOctetAccepted(
3385 req.getHeaderValue("Accept")))
3386 {
3387 asyncResp->res.result(
3388 boost::beast::http::status::bad_request);
3389 return;
3390 }
3391
3392 uint64_t currentValue = 0;
3393 uint16_t index = 0;
3394 if (!parsePostCode(postCodeID, currentValue, index))
3395 {
3396 messages::resourceNotFound(asyncResp->res, "LogEntry",
3397 postCodeID);
3398 return;
3399 }
3400
3401 crow::connections::systemBus->async_method_call(
3402 [asyncResp, postCodeID, currentValue](
3403 const boost::system::error_code ec,
3404 const std::vector<std::tuple<
3405 uint64_t, std::vector<uint8_t>>>& postcodes) {
3406 if (ec.value() == EBADR)
3407 {
3408 messages::resourceNotFound(asyncResp->res,
3409 "LogEntry", postCodeID);
3410 return;
3411 }
3412 if (ec)
3413 {
3414 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3415 messages::internalError(asyncResp->res);
3416 return;
3417 }
3418
3419 size_t value = static_cast<size_t>(currentValue) - 1;
3420 if (value == std::string::npos ||
3421 postcodes.size() < currentValue)
3422 {
3423 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3424 messages::resourceNotFound(asyncResp->res,
3425 "LogEntry", postCodeID);
3426 return;
3427 }
3428
Ed Tanous9eb808c2022-01-25 10:19:23 -08003429 const auto& [tID, c] = postcodes[value];
Ed Tanous46ff87b2022-01-07 09:25:51 -08003430 if (c.empty())
George Liu647b3cd2021-07-05 12:43:56 +08003431 {
3432 BMCWEB_LOG_INFO << "No found post code data";
3433 messages::resourceNotFound(asyncResp->res,
3434 "LogEntry", postCodeID);
3435 return;
3436 }
Ed Tanous46ff87b2022-01-07 09:25:51 -08003437 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3438 const char* d = reinterpret_cast<const char*>(c.data());
3439 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003440
3441 asyncResp->res.addHeader("Content-Type",
3442 "application/octet-stream");
3443 asyncResp->res.addHeader("Content-Transfer-Encoding",
3444 "Base64");
3445 asyncResp->res.body() =
3446 crow::utility::base64encode(strData);
3447 },
3448 "xyz.openbmc_project.State.Boot.PostCode0",
3449 "/xyz/openbmc_project/State/Boot/PostCode0",
3450 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3451 index);
3452 });
3453}
3454
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003455inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003456{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003457 BMCWEB_ROUTE(
3458 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003459 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003460 .methods(boost::beast::http::verb::get)(
3461 [](const crow::Request&,
3462 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3463 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003464 uint16_t bootIndex = 0;
3465 uint64_t codeIndex = 0;
3466 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003467 {
3468 // Requested ID was not found
3469 messages::resourceMissingAtURI(asyncResp->res, targetID);
3470 return;
3471 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003472 if (bootIndex == 0 || codeIndex == 0)
3473 {
3474 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3475 << targetID;
3476 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003477
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003478 asyncResp->res.jsonValue["@odata.type"] =
3479 "#LogEntry.v1_4_0.LogEntry";
3480 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08003481 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003482 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3483 asyncResp->res.jsonValue["Description"] =
3484 "Collection of POST Code Log Entries";
3485 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3486 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003487
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003488 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3489 });
3490}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003491
Ed Tanous1da66f72018-07-27 16:13:37 -07003492} // namespace redfish