blob: 0bfd15e8e6b0ed48368d9b1f6b294f858c0e00ab [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 Tanous271584a2019-07-09 16:24:22 -0700139 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500140 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700141 if (ret < 0)
142 {
143 return ret;
144 }
Ed Tanous39e77502019-03-04 17:35:53 -0800145 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700146 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000147 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700148 return ret;
149}
150
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700151inline static int getJournalMetadata(sd_journal* journal,
152 const std::string_view& field,
153 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700154{
155 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800156 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700157 // Get the metadata from the requested field of the journal entry
158 ret = getJournalMetadata(journal, field, metadata);
159 if (ret < 0)
160 {
161 return ret;
162 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000163 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700164 return ret;
165}
166
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700167inline static bool getEntryTimestamp(sd_journal* journal,
168 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800169{
170 int ret = 0;
171 uint64_t timestamp = 0;
172 ret = sd_journal_get_realtime_usec(journal, &timestamp);
173 if (ret < 0)
174 {
175 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
176 << strerror(-ret);
177 return false;
178 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800179 entryTimestamp = crow::utility::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500180 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800181}
182
zhanghch058d1b46d2021-04-01 11:18:24 +0800183static bool getSkipParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
184 const crow::Request& req, uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700185{
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700186 boost::urls::query_params_view::iterator it = req.urlParams.find("$skip");
James Feist5a7e8772020-07-22 09:08:38 -0700187 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700188 {
James Feist5a7e8772020-07-22 09:08:38 -0700189 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500190 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700191 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
192 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700193 {
194
zhanghch058d1b46d2021-04-01 11:18:24 +0800195 messages::queryParameterValueTypeError(
196 asyncResp->res, std::string(skipParam), "$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700197 return false;
198 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700199 }
200 return true;
201}
202
Ed Tanous271584a2019-07-09 16:24:22 -0700203static constexpr const uint64_t maxEntriesPerPage = 1000;
zhanghch058d1b46d2021-04-01 11:18:24 +0800204static bool getTopParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
205 const crow::Request& req, uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700206{
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700207 boost::urls::query_params_view::iterator it = req.urlParams.find("$top");
James Feist5a7e8772020-07-22 09:08:38 -0700208 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700209 {
James Feist5a7e8772020-07-22 09:08:38 -0700210 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500211 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700212 top = std::strtoul(topParam.c_str(), &ptr, 10);
213 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700214 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800215 messages::queryParameterValueTypeError(
216 asyncResp->res, std::string(topParam), "$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700217 return false;
218 }
Ed Tanous271584a2019-07-09 16:24:22 -0700219 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700220 {
221
222 messages::queryParameterOutOfRange(
zhanghch058d1b46d2021-04-01 11:18:24 +0800223 asyncResp->res, std::to_string(top), "$top",
Jason M. Bills16428a12018-11-02 12:42:29 -0700224 "1-" + std::to_string(maxEntriesPerPage));
225 return false;
226 }
227 }
228 return true;
229}
230
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700231inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
232 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700233{
234 int ret = 0;
235 static uint64_t prevTs = 0;
236 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700237 if (firstEntry)
238 {
239 prevTs = 0;
240 }
241
Jason M. Bills16428a12018-11-02 12:42:29 -0700242 // Get the entry timestamp
243 uint64_t curTs = 0;
244 ret = sd_journal_get_realtime_usec(journal, &curTs);
245 if (ret < 0)
246 {
247 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
248 << strerror(-ret);
249 return false;
250 }
251 // If the timestamp isn't unique, increment the index
252 if (curTs == prevTs)
253 {
254 index++;
255 }
256 else
257 {
258 // Otherwise, reset it
259 index = 0;
260 }
261 // Save the timestamp
262 prevTs = curTs;
263
264 entryID = std::to_string(curTs);
265 if (index > 0)
266 {
267 entryID += "_" + std::to_string(index);
268 }
269 return true;
270}
271
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500272static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700273 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700274{
Ed Tanous271584a2019-07-09 16:24:22 -0700275 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700276 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700277 if (firstEntry)
278 {
279 prevTs = 0;
280 }
281
Jason M. Bills95820182019-04-22 16:25:34 -0700282 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700283 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700284 std::tm timeStruct = {};
285 std::istringstream entryStream(logEntry);
286 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
287 {
288 curTs = std::mktime(&timeStruct);
289 }
290 // If the timestamp isn't unique, increment the index
291 if (curTs == prevTs)
292 {
293 index++;
294 }
295 else
296 {
297 // Otherwise, reset it
298 index = 0;
299 }
300 // Save the timestamp
301 prevTs = curTs;
302
303 entryID = std::to_string(curTs);
304 if (index > 0)
305 {
306 entryID += "_" + std::to_string(index);
307 }
308 return true;
309}
310
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700311inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800312 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
313 const std::string& entryID, uint64_t& timestamp,
314 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700315{
316 if (entryID.empty())
317 {
318 return false;
319 }
320 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800321 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700322
Ed Tanous81ce6092020-12-17 16:54:55 +0000323 auto underscorePos = tsStr.find('_');
Jason M. Bills16428a12018-11-02 12:42:29 -0700324 if (underscorePos != tsStr.npos)
325 {
326 // Timestamp has an index
327 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800328 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700329 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700330 auto [ptr, ec] = std::from_chars(
331 indexStr.data(), indexStr.data() + indexStr.size(), index);
332 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700333 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800334 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700335 return false;
336 }
337 }
338 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700339 auto [ptr, ec] =
340 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
341 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700342 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800343 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700344 return false;
345 }
346 return true;
347}
348
Jason M. Bills95820182019-04-22 16:25:34 -0700349static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500350 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700351{
352 static const std::filesystem::path redfishLogDir = "/var/log";
353 static const std::string redfishLogFilename = "redfish";
354
355 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500356 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700357 std::filesystem::directory_iterator(redfishLogDir))
358 {
359 // If we find a redfish log file, save the path
360 std::string filename = dirEnt.path().filename();
361 if (boost::starts_with(filename, redfishLogFilename))
362 {
363 redfishLogFiles.emplace_back(redfishLogDir / filename);
364 }
365 }
366 // As the log files rotate, they are appended with a ".#" that is higher for
367 // the older logs. Since we don't expect more than 10 log files, we
368 // can just sort the list to get them in order from newest to oldest
369 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
370
371 return !redfishLogFiles.empty();
372}
373
zhanghch058d1b46d2021-04-01 11:18:24 +0800374inline void
375 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
376 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500377{
378 std::string dumpPath;
379 if (dumpType == "BMC")
380 {
381 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
382 }
383 else if (dumpType == "System")
384 {
385 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
386 }
387 else
388 {
389 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
390 messages::internalError(asyncResp->res);
391 return;
392 }
393
394 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800395 [asyncResp, dumpPath,
396 dumpType](const boost::system::error_code ec,
397 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500398 if (ec)
399 {
400 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
401 messages::internalError(asyncResp->res);
402 return;
403 }
404
405 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
406 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500407 std::string dumpEntryPath =
408 "/xyz/openbmc_project/dump/" +
409 std::string(boost::algorithm::to_lower_copy(dumpType)) +
410 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500411
412 for (auto& object : resp)
413 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500414 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500415 {
416 continue;
417 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800418 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500419 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500420 std::string dumpStatus;
421 nlohmann::json thisEntry;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000422
423 std::string entryID = object.first.filename();
424 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500425 {
426 continue;
427 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500428
429 for (auto& interfaceMap : object.second)
430 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500431 if (interfaceMap.first ==
432 "xyz.openbmc_project.Common.Progress")
433 {
434 for (auto& propertyMap : interfaceMap.second)
435 {
436 if (propertyMap.first == "Status")
437 {
438 auto status = std::get_if<std::string>(
439 &propertyMap.second);
440 if (status == nullptr)
441 {
442 messages::internalError(asyncResp->res);
443 break;
444 }
445 dumpStatus = *status;
446 }
447 }
448 }
449 else if (interfaceMap.first ==
450 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500451 {
452
453 for (auto& propertyMap : interfaceMap.second)
454 {
455 if (propertyMap.first == "Size")
456 {
457 auto sizePtr =
458 std::get_if<uint64_t>(&propertyMap.second);
459 if (sizePtr == nullptr)
460 {
461 messages::internalError(asyncResp->res);
462 break;
463 }
464 size = *sizePtr;
465 break;
466 }
467 }
468 }
469 else if (interfaceMap.first ==
470 "xyz.openbmc_project.Time.EpochTime")
471 {
472
473 for (auto& propertyMap : interfaceMap.second)
474 {
475 if (propertyMap.first == "Elapsed")
476 {
477 const uint64_t* usecsTimeStamp =
478 std::get_if<uint64_t>(&propertyMap.second);
479 if (usecsTimeStamp == nullptr)
480 {
481 messages::internalError(asyncResp->res);
482 break;
483 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800484 timestamp = (*usecsTimeStamp / 1000 / 1000);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500485 break;
486 }
487 }
488 }
489 }
490
George Liu0fda0f12021-11-16 10:06:17 +0800491 if (dumpStatus !=
492 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500493 !dumpStatus.empty())
494 {
495 // Dump status is not Complete, no need to enumerate
496 continue;
497 }
498
George Liu647b3cd2021-07-05 12:43:56 +0800499 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500500 thisEntry["@odata.id"] = dumpPath + entryID;
501 thisEntry["Id"] = entryID;
502 thisEntry["EntryType"] = "Event";
Nan Zhou1d8782e2021-11-29 22:23:18 -0800503 thisEntry["Created"] =
504 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500505 thisEntry["Name"] = dumpType + " Dump Entry";
506
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500507 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500508
509 if (dumpType == "BMC")
510 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500511 thisEntry["DiagnosticDataType"] = "Manager";
512 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500513 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
514 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500515 }
516 else if (dumpType == "System")
517 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500518 thisEntry["DiagnosticDataType"] = "OEM";
519 thisEntry["OEMDiagnosticDataType"] = "System";
520 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500521 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
522 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500523 }
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500524 entriesArray.push_back(std::move(thisEntry));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500525 }
526 asyncResp->res.jsonValue["Members@odata.count"] =
527 entriesArray.size();
528 },
529 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
530 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
531}
532
zhanghch058d1b46d2021-04-01 11:18:24 +0800533inline void
534 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
535 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500536{
537 std::string dumpPath;
538 if (dumpType == "BMC")
539 {
540 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
541 }
542 else if (dumpType == "System")
543 {
544 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
545 }
546 else
547 {
548 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
549 messages::internalError(asyncResp->res);
550 return;
551 }
552
553 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800554 [asyncResp, entryID, dumpPath,
555 dumpType](const boost::system::error_code ec,
556 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500557 if (ec)
558 {
559 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
560 messages::internalError(asyncResp->res);
561 return;
562 }
563
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500564 bool foundDumpEntry = false;
565 std::string dumpEntryPath =
566 "/xyz/openbmc_project/dump/" +
567 std::string(boost::algorithm::to_lower_copy(dumpType)) +
568 "/entry/";
569
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500570 for (auto& objectPath : resp)
571 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500572 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500573 {
574 continue;
575 }
576
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500577 foundDumpEntry = true;
Nan Zhou1d8782e2021-11-29 22:23:18 -0800578 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500579 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500580 std::string dumpStatus;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500581
582 for (auto& interfaceMap : objectPath.second)
583 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500584 if (interfaceMap.first ==
585 "xyz.openbmc_project.Common.Progress")
586 {
587 for (auto& propertyMap : interfaceMap.second)
588 {
589 if (propertyMap.first == "Status")
590 {
591 auto status = std::get_if<std::string>(
592 &propertyMap.second);
593 if (status == nullptr)
594 {
595 messages::internalError(asyncResp->res);
596 break;
597 }
598 dumpStatus = *status;
599 }
600 }
601 }
602 else if (interfaceMap.first ==
603 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500604 {
605 for (auto& propertyMap : interfaceMap.second)
606 {
607 if (propertyMap.first == "Size")
608 {
609 auto sizePtr =
610 std::get_if<uint64_t>(&propertyMap.second);
611 if (sizePtr == nullptr)
612 {
613 messages::internalError(asyncResp->res);
614 break;
615 }
616 size = *sizePtr;
617 break;
618 }
619 }
620 }
621 else if (interfaceMap.first ==
622 "xyz.openbmc_project.Time.EpochTime")
623 {
624 for (auto& propertyMap : interfaceMap.second)
625 {
626 if (propertyMap.first == "Elapsed")
627 {
628 const uint64_t* usecsTimeStamp =
629 std::get_if<uint64_t>(&propertyMap.second);
630 if (usecsTimeStamp == nullptr)
631 {
632 messages::internalError(asyncResp->res);
633 break;
634 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800635 timestamp = *usecsTimeStamp / 1000 / 1000;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500636 break;
637 }
638 }
639 }
640 }
641
George Liu0fda0f12021-11-16 10:06:17 +0800642 if (dumpStatus !=
643 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500644 !dumpStatus.empty())
645 {
646 // Dump status is not Complete
647 // return not found until status is changed to Completed
648 messages::resourceNotFound(asyncResp->res,
649 dumpType + " dump", entryID);
650 return;
651 }
652
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500653 asyncResp->res.jsonValue["@odata.type"] =
George Liu647b3cd2021-07-05 12:43:56 +0800654 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500655 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
656 asyncResp->res.jsonValue["Id"] = entryID;
657 asyncResp->res.jsonValue["EntryType"] = "Event";
658 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800659 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500660 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
661
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500662 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500663
664 if (dumpType == "BMC")
665 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500666 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
667 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500668 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
669 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500670 }
671 else if (dumpType == "System")
672 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500673 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
674 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500675 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500676 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500677 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
678 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500679 }
680 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500681 if (foundDumpEntry == false)
682 {
683 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
684 messages::internalError(asyncResp->res);
685 return;
686 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500687 },
688 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
689 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
690}
691
zhanghch058d1b46d2021-04-01 11:18:24 +0800692inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800693 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500694 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500695{
George Liu3de8d8b2021-03-22 17:49:39 +0800696 auto respHandler = [asyncResp,
697 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500698 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
699 if (ec)
700 {
George Liu3de8d8b2021-03-22 17:49:39 +0800701 if (ec.value() == EBADR)
702 {
703 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
704 return;
705 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500706 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
707 << ec;
708 messages::internalError(asyncResp->res);
709 return;
710 }
711 };
712 crow::connections::systemBus->async_method_call(
713 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500714 "/xyz/openbmc_project/dump/" +
715 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
716 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500717 "xyz.openbmc_project.Object.Delete", "Delete");
718}
719
zhanghch058d1b46d2021-04-01 11:18:24 +0800720inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700721 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800722 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
723 const uint32_t& dumpId, const std::string& dumpPath,
724 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500725{
726 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500727 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500728 boost::system::error_code err, sdbusplus::message::message& m,
729 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000730 if (err)
731 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500732 BMCWEB_LOG_ERROR << "Error in creating a dump";
733 taskData->state = "Cancelled";
734 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000735 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500736 std::vector<std::pair<
Ed Tanous168e20c2021-12-13 14:39:53 -0800737 std::string, std::vector<std::pair<
738 std::string, dbus::utility::DbusVariantType>>>>
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500739 interfacesList;
740
741 sdbusplus::message::object_path objPath;
742
743 m.read(objPath, interfacesList);
744
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500745 if (objPath.str ==
746 "/xyz/openbmc_project/dump/" +
747 std::string(boost::algorithm::to_lower_copy(dumpType)) +
748 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500749 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500750 nlohmann::json retMessage = messages::success();
751 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500752
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500753 std::string headerLoc =
754 "Location: " + dumpPath + std::to_string(dumpId);
755 taskData->payload->httpHeaders.emplace_back(
756 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500757
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500758 taskData->state = "Completed";
759 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500760 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500761 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500762 },
763 "type='signal',interface='org.freedesktop.DBus."
764 "ObjectManager',"
765 "member='InterfacesAdded', "
766 "path='/xyz/openbmc_project/dump'");
767
768 task->startTimer(std::chrono::minutes(3));
769 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700770 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500771}
772
zhanghch058d1b46d2021-04-01 11:18:24 +0800773inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
774 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500775{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500776
777 std::string dumpPath;
778 if (dumpType == "BMC")
779 {
780 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
781 }
782 else if (dumpType == "System")
783 {
784 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
785 }
786 else
787 {
788 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
789 messages::internalError(asyncResp->res);
790 return;
791 }
792
793 std::optional<std::string> diagnosticDataType;
794 std::optional<std::string> oemDiagnosticDataType;
795
796 if (!redfish::json_util::readJson(
797 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
798 "OEMDiagnosticDataType", oemDiagnosticDataType))
799 {
800 return;
801 }
802
803 if (dumpType == "System")
804 {
805 if (!oemDiagnosticDataType || !diagnosticDataType)
806 {
807 BMCWEB_LOG_ERROR << "CreateDump action parameter "
808 "'DiagnosticDataType'/"
809 "'OEMDiagnosticDataType' value not found!";
810 messages::actionParameterMissing(
811 asyncResp->res, "CollectDiagnosticData",
812 "DiagnosticDataType & OEMDiagnosticDataType");
813 return;
814 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700815 if ((*oemDiagnosticDataType != "System") ||
816 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500817 {
818 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
819 messages::invalidObject(asyncResp->res,
820 "System Dump creation parameters");
821 return;
822 }
823 }
824 else if (dumpType == "BMC")
825 {
826 if (!diagnosticDataType)
827 {
George Liu0fda0f12021-11-16 10:06:17 +0800828 BMCWEB_LOG_ERROR
829 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500830 messages::actionParameterMissing(
831 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
832 return;
833 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700834 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500835 {
836 BMCWEB_LOG_ERROR
837 << "Wrong parameter value passed for 'DiagnosticDataType'";
838 messages::invalidObject(asyncResp->res,
839 "BMC Dump creation parameters");
840 return;
841 }
842 }
843
844 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700845 [asyncResp, payload(task::Payload(req)), dumpPath,
846 dumpType](const boost::system::error_code ec,
847 const uint32_t& dumpId) mutable {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500848 if (ec)
849 {
850 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
851 messages::internalError(asyncResp->res);
852 return;
853 }
854 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
855
Ed Tanous98be3e32021-09-16 15:05:36 -0700856 createDumpTaskCallback(std::move(payload), asyncResp, dumpId,
857 dumpPath, dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500858 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500859 "xyz.openbmc_project.Dump.Manager",
860 "/xyz/openbmc_project/dump/" +
861 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500862 "xyz.openbmc_project.Dump.Create", "CreateDump");
863}
864
zhanghch058d1b46d2021-04-01 11:18:24 +0800865inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
866 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500867{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500868 std::string dumpTypeLowerCopy =
869 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800870
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500871 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500872 [asyncResp, dumpType](const boost::system::error_code ec,
873 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500874 if (ec)
875 {
876 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
877 messages::internalError(asyncResp->res);
878 return;
879 }
880
881 for (const std::string& path : subTreePaths)
882 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000883 sdbusplus::message::object_path objPath(path);
884 std::string logID = objPath.filename();
885 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500886 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000887 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500888 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000889 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500890 }
891 },
892 "xyz.openbmc_project.ObjectMapper",
893 "/xyz/openbmc_project/object_mapper",
894 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500895 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
896 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
897 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500898}
899
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700900inline static void parseCrashdumpParameters(
Ed Tanous168e20c2021-12-13 14:39:53 -0800901 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
902 params,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500903 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700904{
905 for (auto property : params)
906 {
907 if (property.first == "Timestamp")
908 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500909 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500910 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700911 if (value != nullptr)
912 {
913 timestamp = *value;
914 }
915 }
916 else if (property.first == "Filename")
917 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500918 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500919 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700920 if (value != nullptr)
921 {
922 filename = *value;
923 }
924 }
925 else if (property.first == "Log")
926 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500927 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500928 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700929 if (value != nullptr)
930 {
931 logfile = *value;
932 }
933 }
934 }
935}
936
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500937constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700938inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700939{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800940 /**
941 * Functions triggers appropriate requests on DBus
942 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700943 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700944 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700945 .methods(boost::beast::http::verb::get)(
946 [](const crow::Request&,
947 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
948
949 {
950 // Collections don't include the static data added by SubRoute
951 // because it has a duplicate entry for members
952 asyncResp->res.jsonValue["@odata.type"] =
953 "#LogServiceCollection.LogServiceCollection";
954 asyncResp->res.jsonValue["@odata.id"] =
955 "/redfish/v1/Systems/system/LogServices";
956 asyncResp->res.jsonValue["Name"] =
957 "System Log Services Collection";
958 asyncResp->res.jsonValue["Description"] =
959 "Collection of LogServices for this Computer System";
960 nlohmann::json& logServiceArray =
961 asyncResp->res.jsonValue["Members"];
962 logServiceArray = nlohmann::json::array();
963 logServiceArray.push_back(
964 {{"@odata.id",
965 "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500966#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700967 logServiceArray.push_back(
968 {{"@odata.id",
969 "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600970#endif
971
Jason M. Billsd53dd412019-02-12 17:16:22 -0800972#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700973 logServiceArray.push_back(
974 {{"@odata.id",
975 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800976#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800977
978#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
979 logServiceArray.push_back(
980 {{"@odata.id",
981 "/redfish/v1/Systems/system/LogServices/HostLogger"}});
982#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700983 asyncResp->res.jsonValue["Members@odata.count"] =
984 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800985
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700986 crow::connections::systemBus->async_method_call(
987 [asyncResp](const boost::system::error_code ec,
988 const std::vector<std::string>& subtreePath) {
989 if (ec)
990 {
991 BMCWEB_LOG_ERROR << ec;
992 return;
993 }
ZhikuiRena3316fc2020-01-29 14:58:08 -0800994
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700995 for (auto& pathStr : subtreePath)
996 {
997 if (pathStr.find("PostCode") != std::string::npos)
998 {
999 nlohmann::json& logServiceArrayLocal =
1000 asyncResp->res.jsonValue["Members"];
1001 logServiceArrayLocal.push_back(
George Liu0fda0f12021-11-16 10:06:17 +08001002 {{"@odata.id",
1003 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001004 asyncResp->res
1005 .jsonValue["Members@odata.count"] =
1006 logServiceArrayLocal.size();
1007 return;
1008 }
1009 }
1010 },
1011 "xyz.openbmc_project.ObjectMapper",
1012 "/xyz/openbmc_project/object_mapper",
1013 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
1014 0, std::array<const char*, 1>{postCodeIface});
1015 });
1016}
1017
1018inline void requestRoutesEventLogService(App& app)
1019{
1020 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -07001021 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001022 .methods(
1023 boost::beast::http::verb::
1024 get)([](const crow::Request&,
1025 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1026 asyncResp->res.jsonValue["@odata.id"] =
1027 "/redfish/v1/Systems/system/LogServices/EventLog";
1028 asyncResp->res.jsonValue["@odata.type"] =
1029 "#LogService.v1_1_0.LogService";
1030 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1031 asyncResp->res.jsonValue["Description"] =
1032 "System Event Log Service";
1033 asyncResp->res.jsonValue["Id"] = "EventLog";
1034 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301035
1036 std::pair<std::string, std::string> redfishDateTimeOffset =
1037 crow::utility::getDateTimeOffsetNow();
1038
1039 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1040 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1041 redfishDateTimeOffset.second;
1042
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001043 asyncResp->res.jsonValue["Entries"] = {
1044 {"@odata.id",
1045 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1046 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1047
George Liu0fda0f12021-11-16 10:06:17 +08001048 {"target",
1049 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001050 });
1051}
1052
1053inline void requestRoutesJournalEventLogClear(App& app)
1054{
1055 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1056 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001057 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001058 .methods(boost::beast::http::verb::post)(
1059 [](const crow::Request&,
1060 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1061 // Clear the EventLog by deleting the log files
1062 std::vector<std::filesystem::path> redfishLogFiles;
1063 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001064 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001065 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001066 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001067 std::error_code ec;
1068 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001069 }
1070 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001071
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001072 // Reload rsyslog so it knows to start new log files
1073 crow::connections::systemBus->async_method_call(
1074 [asyncResp](const boost::system::error_code ec) {
1075 if (ec)
1076 {
1077 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1078 << ec;
1079 messages::internalError(asyncResp->res);
1080 return;
1081 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001082
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001083 messages::success(asyncResp->res);
1084 },
1085 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1086 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1087 "rsyslog.service", "replace");
1088 });
1089}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001090
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001091static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001092 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001093 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001094{
Jason M. Bills95820182019-04-22 16:25:34 -07001095 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001096 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001097 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001098 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001099 {
1100 return 1;
1101 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001102 std::string timestamp = logEntry.substr(0, space);
1103 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001104 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001105 if (entryStart == std::string::npos)
1106 {
1107 return 1;
1108 }
1109 std::string_view entry(logEntry);
1110 entry.remove_prefix(entryStart);
1111 // Use split to separate the entry into its fields
1112 std::vector<std::string> logEntryFields;
1113 boost::split(logEntryFields, entry, boost::is_any_of(","),
1114 boost::token_compress_on);
1115 // We need at least a MessageId to be valid
1116 if (logEntryFields.size() < 1)
1117 {
1118 return 1;
1119 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001120 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001121
Jason M. Bills4851d452019-03-28 11:27:48 -07001122 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001123 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001124 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001125
Jason M. Bills4851d452019-03-28 11:27:48 -07001126 std::string msg;
1127 std::string severity;
1128 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001129 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001130 msg = message->message;
1131 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001132 }
1133
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001134 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001135 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001136 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001137 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001138 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001139 // If the first string is empty, assume there are no MessageArgs
1140 std::size_t messageArgsSize = 0;
1141 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001142 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001143 messageArgsSize = logEntryFields.size() - 1;
1144 }
1145
Ed Tanous23a21a12020-07-25 04:45:05 +00001146 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001147
1148 // Fill the MessageArgs into the Message
1149 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001150 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001151 {
1152 std::string argStr = "%" + std::to_string(++i);
1153 size_t argPos = msg.find(argStr);
1154 if (argPos != std::string::npos)
1155 {
1156 msg.replace(argPos, argStr.length(), messageArg);
1157 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001158 }
1159 }
1160
Jason M. Bills95820182019-04-22 16:25:34 -07001161 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1162 // format which matches the Redfish format except for the fractional seconds
1163 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001164 std::size_t dot = timestamp.find_first_of('.');
1165 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001166 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001167 {
Jason M. Bills95820182019-04-22 16:25:34 -07001168 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001169 }
1170
1171 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001172 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001173 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001174 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001175 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001176 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001177 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001178 {"Id", logEntryID},
1179 {"Message", std::move(msg)},
1180 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001181 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001182 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001183 {"Severity", std::move(severity)},
1184 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001185 return 0;
1186}
1187
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001188inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001189{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001190 BMCWEB_ROUTE(app,
1191 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001192 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001193 .methods(boost::beast::http::verb::get)(
1194 [](const crow::Request& req,
1195 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1196 uint64_t skip = 0;
1197 uint64_t top = maxEntriesPerPage; // Show max entries by default
1198 if (!getSkipParam(asyncResp, req, skip))
Jason M. Bills95820182019-04-22 16:25:34 -07001199 {
Jason M. Bills95820182019-04-22 16:25:34 -07001200 return;
1201 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001202 if (!getTopParam(asyncResp, req, top))
Jason M. Bills897967d2019-07-29 17:05:30 -07001203 {
Jason M. Bills897967d2019-07-29 17:05:30 -07001204 return;
1205 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001206 // Collections don't include the static data added by SubRoute
1207 // because it has a duplicate entry for members
1208 asyncResp->res.jsonValue["@odata.type"] =
1209 "#LogEntryCollection.LogEntryCollection";
1210 asyncResp->res.jsonValue["@odata.id"] =
1211 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1212 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1213 asyncResp->res.jsonValue["Description"] =
1214 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001215
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001216 nlohmann::json& logEntryArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001217 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001218 logEntryArray = nlohmann::json::array();
1219 // Go through the log files and create a unique ID for each
1220 // entry
1221 std::vector<std::filesystem::path> redfishLogFiles;
1222 getRedfishLogFiles(redfishLogFiles);
1223 uint64_t entryCount = 0;
1224 std::string logEntry;
1225
1226 // Oldest logs are in the last file, so start there and loop
1227 // backwards
1228 for (auto it = redfishLogFiles.rbegin();
1229 it < redfishLogFiles.rend(); it++)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001230 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001231 std::ifstream logStream(*it);
1232 if (!logStream.is_open())
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001233 {
1234 continue;
1235 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001236
1237 // Reset the unique ID on the first entry
1238 bool firstEntry = true;
1239 while (std::getline(logStream, logEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001240 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001241 entryCount++;
1242 // Handle paging using skip (number of entries to skip
1243 // from the start) and top (number of entries to
1244 // display)
1245 if (entryCount <= skip || entryCount > skip + top)
George Liuebd45902020-08-26 14:21:10 +08001246 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001247 continue;
George Liuebd45902020-08-26 14:21:10 +08001248 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001249
1250 std::string idStr;
1251 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
George Liuebd45902020-08-26 14:21:10 +08001252 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001253 continue;
George Liuebd45902020-08-26 14:21:10 +08001254 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001255
1256 if (firstEntry)
1257 {
1258 firstEntry = false;
1259 }
1260
1261 logEntryArray.push_back({});
1262 nlohmann::json& bmcLogEntry = logEntryArray.back();
1263 if (fillEventLogEntryJson(idStr, logEntry,
1264 bmcLogEntry) != 0)
Xiaochao Ma75710de2021-01-21 17:56:02 +08001265 {
1266 messages::internalError(asyncResp->res);
1267 return;
1268 }
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001269 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001270 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001271 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1272 if (skip + top < entryCount)
Ed Tanous271584a2019-07-09 16:24:22 -07001273 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001274 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001275 "/redfish/v1/Systems/system/LogServices/EventLog/"
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001276 "Entries?$skip=" +
1277 std::to_string(skip + top);
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001278 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001279 });
1280}
Chicago Duan336e96c2019-07-15 14:22:08 +08001281
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001282inline void requestRoutesJournalEventLogEntry(App& app)
1283{
1284 BMCWEB_ROUTE(
1285 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001286 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001287 .methods(boost::beast::http::verb::get)(
1288 [](const crow::Request&,
1289 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1290 const std::string& param) {
1291 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001292
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001293 // Go through the log files and check the unique ID for each
1294 // entry to find the target entry
1295 std::vector<std::filesystem::path> redfishLogFiles;
1296 getRedfishLogFiles(redfishLogFiles);
1297 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001298
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001299 // Oldest logs are in the last file, so start there and loop
1300 // backwards
1301 for (auto it = redfishLogFiles.rbegin();
1302 it < redfishLogFiles.rend(); it++)
1303 {
1304 std::ifstream logStream(*it);
1305 if (!logStream.is_open())
1306 {
1307 continue;
1308 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001309
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001310 // Reset the unique ID on the first entry
1311 bool firstEntry = true;
1312 while (std::getline(logStream, logEntry))
1313 {
1314 std::string idStr;
1315 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1316 {
1317 continue;
1318 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001319
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001320 if (firstEntry)
1321 {
1322 firstEntry = false;
1323 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001324
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001325 if (idStr == targetID)
1326 {
1327 if (fillEventLogEntryJson(
1328 idStr, logEntry,
1329 asyncResp->res.jsonValue) != 0)
1330 {
1331 messages::internalError(asyncResp->res);
1332 return;
1333 }
1334 return;
1335 }
1336 }
1337 }
1338 // Requested ID was not found
1339 messages::resourceMissingAtURI(asyncResp->res, targetID);
1340 });
1341}
1342
1343inline void requestRoutesDBusEventLogEntryCollection(App& app)
1344{
1345 BMCWEB_ROUTE(app,
1346 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001347 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001348 .methods(
1349 boost::beast::http::verb::
1350 get)([](const crow::Request&,
1351 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1352 // Collections don't include the static data added by SubRoute
1353 // because it has a duplicate entry for members
1354 asyncResp->res.jsonValue["@odata.type"] =
1355 "#LogEntryCollection.LogEntryCollection";
1356 asyncResp->res.jsonValue["@odata.id"] =
1357 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1358 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1359 asyncResp->res.jsonValue["Description"] =
1360 "Collection of System Event Log Entries";
1361
1362 // DBus implementation of EventLog/Entries
1363 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001364 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001365 [asyncResp](const boost::system::error_code ec,
Ed Tanous711ac7a2021-12-20 09:34:41 -08001366 dbus::utility::ManagedObjectType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001367 if (ec)
1368 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001369 // TODO Handle for specific error code
1370 BMCWEB_LOG_ERROR
1371 << "getLogEntriesIfaceData resp_handler got error "
1372 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001373 messages::internalError(asyncResp->res);
1374 return;
1375 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001376 nlohmann::json& entriesArray =
1377 asyncResp->res.jsonValue["Members"];
1378 entriesArray = nlohmann::json::array();
1379 for (auto& objectPath : resp)
1380 {
1381 uint32_t* id = nullptr;
1382 std::time_t timestamp{};
1383 std::time_t updateTimestamp{};
1384 std::string* severity = nullptr;
1385 std::string* message = nullptr;
1386 std::string* filePath = nullptr;
1387 bool resolved = false;
1388 for (auto& interfaceMap : objectPath.second)
1389 {
1390 if (interfaceMap.first ==
1391 "xyz.openbmc_project.Logging.Entry")
1392 {
1393 for (auto& propertyMap : interfaceMap.second)
1394 {
1395 if (propertyMap.first == "Id")
1396 {
1397 id = std::get_if<uint32_t>(
1398 &propertyMap.second);
1399 }
1400 else if (propertyMap.first == "Timestamp")
1401 {
1402 const uint64_t* millisTimeStamp =
1403 std::get_if<uint64_t>(
1404 &propertyMap.second);
1405 if (millisTimeStamp != nullptr)
1406 {
1407 timestamp =
1408 crow::utility::getTimestamp(
1409 *millisTimeStamp);
1410 }
1411 }
1412 else if (propertyMap.first ==
1413 "UpdateTimestamp")
1414 {
1415 const uint64_t* millisTimeStamp =
1416 std::get_if<uint64_t>(
1417 &propertyMap.second);
1418 if (millisTimeStamp != nullptr)
1419 {
1420 updateTimestamp =
1421 crow::utility::getTimestamp(
1422 *millisTimeStamp);
1423 }
1424 }
1425 else if (propertyMap.first == "Severity")
1426 {
1427 severity = std::get_if<std::string>(
1428 &propertyMap.second);
1429 }
1430 else if (propertyMap.first == "Message")
1431 {
1432 message = std::get_if<std::string>(
1433 &propertyMap.second);
1434 }
1435 else if (propertyMap.first == "Resolved")
1436 {
1437 bool* resolveptr = std::get_if<bool>(
1438 &propertyMap.second);
1439 if (resolveptr == nullptr)
1440 {
1441 messages::internalError(
1442 asyncResp->res);
1443 return;
1444 }
1445 resolved = *resolveptr;
1446 }
1447 }
1448 if (id == nullptr || message == nullptr ||
1449 severity == nullptr)
1450 {
1451 messages::internalError(asyncResp->res);
1452 return;
1453 }
1454 }
1455 else if (interfaceMap.first ==
1456 "xyz.openbmc_project.Common.FilePath")
1457 {
1458 for (auto& propertyMap : interfaceMap.second)
1459 {
1460 if (propertyMap.first == "Path")
1461 {
1462 filePath = std::get_if<std::string>(
1463 &propertyMap.second);
1464 }
1465 }
1466 }
1467 }
1468 // Object path without the
1469 // xyz.openbmc_project.Logging.Entry interface, ignore
1470 // and continue.
1471 if (id == nullptr || message == nullptr ||
1472 severity == nullptr)
1473 {
1474 continue;
1475 }
1476 entriesArray.push_back({});
1477 nlohmann::json& thisEntry = entriesArray.back();
1478 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1479 thisEntry["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001480 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001481 std::to_string(*id);
1482 thisEntry["Name"] = "System Event Log Entry";
1483 thisEntry["Id"] = std::to_string(*id);
1484 thisEntry["Message"] = *message;
1485 thisEntry["Resolved"] = resolved;
1486 thisEntry["EntryType"] = "Event";
1487 thisEntry["Severity"] =
1488 translateSeverityDbusToRedfish(*severity);
1489 thisEntry["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001490 crow::utility::getDateTimeStdtime(timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001491 thisEntry["Modified"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001492 crow::utility::getDateTimeStdtime(updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001493 if (filePath != nullptr)
1494 {
1495 thisEntry["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001496 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001497 std::to_string(*id) + "/attachment";
1498 }
1499 }
1500 std::sort(entriesArray.begin(), entriesArray.end(),
1501 [](const nlohmann::json& left,
1502 const nlohmann::json& right) {
1503 return (left["Id"] <= right["Id"]);
1504 });
1505 asyncResp->res.jsonValue["Members@odata.count"] =
1506 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001507 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001508 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1509 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1510 });
1511}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001512
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001513inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001514{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001515 BMCWEB_ROUTE(
1516 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001517 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001518 .methods(boost::beast::http::verb::get)(
1519 [](const crow::Request&,
1520 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1521 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001522
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001523 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001524 std::string entryID = param;
1525 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001526
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001527 // DBus implementation of EventLog/Entries
1528 // Make call to Logging Service to find all log entry objects
1529 crow::connections::systemBus->async_method_call(
1530 [asyncResp, entryID](const boost::system::error_code ec,
1531 GetManagedPropertyType& resp) {
1532 if (ec.value() == EBADR)
1533 {
1534 messages::resourceNotFound(
1535 asyncResp->res, "EventLogEntry", entryID);
1536 return;
1537 }
1538 if (ec)
1539 {
George Liu0fda0f12021-11-16 10:06:17 +08001540 BMCWEB_LOG_ERROR
1541 << "EventLogEntry (DBus) resp_handler got error "
1542 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001543 messages::internalError(asyncResp->res);
1544 return;
1545 }
1546 uint32_t* id = nullptr;
1547 std::time_t timestamp{};
1548 std::time_t updateTimestamp{};
1549 std::string* severity = nullptr;
1550 std::string* message = nullptr;
1551 std::string* filePath = nullptr;
1552 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001553
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001554 for (auto& propertyMap : resp)
1555 {
1556 if (propertyMap.first == "Id")
1557 {
1558 id = std::get_if<uint32_t>(&propertyMap.second);
1559 }
1560 else if (propertyMap.first == "Timestamp")
1561 {
1562 const uint64_t* millisTimeStamp =
1563 std::get_if<uint64_t>(&propertyMap.second);
1564 if (millisTimeStamp != nullptr)
1565 {
1566 timestamp = crow::utility::getTimestamp(
1567 *millisTimeStamp);
1568 }
1569 }
1570 else if (propertyMap.first == "UpdateTimestamp")
1571 {
1572 const uint64_t* millisTimeStamp =
1573 std::get_if<uint64_t>(&propertyMap.second);
1574 if (millisTimeStamp != nullptr)
1575 {
1576 updateTimestamp =
1577 crow::utility::getTimestamp(
1578 *millisTimeStamp);
1579 }
1580 }
1581 else if (propertyMap.first == "Severity")
1582 {
1583 severity = std::get_if<std::string>(
1584 &propertyMap.second);
1585 }
1586 else if (propertyMap.first == "Message")
1587 {
1588 message = std::get_if<std::string>(
1589 &propertyMap.second);
1590 }
1591 else if (propertyMap.first == "Resolved")
1592 {
1593 bool* resolveptr =
1594 std::get_if<bool>(&propertyMap.second);
1595 if (resolveptr == nullptr)
1596 {
1597 messages::internalError(asyncResp->res);
1598 return;
1599 }
1600 resolved = *resolveptr;
1601 }
1602 else if (propertyMap.first == "Path")
1603 {
1604 filePath = std::get_if<std::string>(
1605 &propertyMap.second);
1606 }
1607 }
1608 if (id == nullptr || message == nullptr ||
1609 severity == nullptr)
1610 {
1611 messages::internalError(asyncResp->res);
1612 return;
1613 }
1614 asyncResp->res.jsonValue["@odata.type"] =
1615 "#LogEntry.v1_8_0.LogEntry";
1616 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001617 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001618 std::to_string(*id);
1619 asyncResp->res.jsonValue["Name"] =
1620 "System Event Log Entry";
1621 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1622 asyncResp->res.jsonValue["Message"] = *message;
1623 asyncResp->res.jsonValue["Resolved"] = resolved;
1624 asyncResp->res.jsonValue["EntryType"] = "Event";
1625 asyncResp->res.jsonValue["Severity"] =
1626 translateSeverityDbusToRedfish(*severity);
1627 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001628 crow::utility::getDateTimeStdtime(timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001629 asyncResp->res.jsonValue["Modified"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001630 crow::utility::getDateTimeStdtime(updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001631 if (filePath != nullptr)
1632 {
1633 asyncResp->res.jsonValue["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001634 "/redfish/v1/Systems/system/LogServices/EventLog/attachment/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001635 std::to_string(*id);
1636 }
1637 },
1638 "xyz.openbmc_project.Logging",
1639 "/xyz/openbmc_project/logging/entry/" + entryID,
1640 "org.freedesktop.DBus.Properties", "GetAll", "");
1641 });
1642
1643 BMCWEB_ROUTE(
1644 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001645 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001646 .methods(boost::beast::http::verb::patch)(
1647 [](const crow::Request& req,
1648 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1649 const std::string& entryId) {
1650 std::optional<bool> resolved;
1651
1652 if (!json_util::readJson(req, asyncResp->res, "Resolved",
1653 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001654 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001655 return;
1656 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001657 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001658
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001659 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001660 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001661 if (ec)
1662 {
1663 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1664 messages::internalError(asyncResp->res);
1665 return;
1666 }
1667 },
1668 "xyz.openbmc_project.Logging",
1669 "/xyz/openbmc_project/logging/entry/" + entryId,
1670 "org.freedesktop.DBus.Properties", "Set",
1671 "xyz.openbmc_project.Logging.Entry", "Resolved",
Ed Tanous168e20c2021-12-13 14:39:53 -08001672 dbus::utility::DbusVariantType(*resolved));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001673 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001674
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001675 BMCWEB_ROUTE(
1676 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001677 .privileges(redfish::privileges::deleteLogEntry)
1678
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001679 .methods(boost::beast::http::verb::delete_)(
1680 [](const crow::Request&,
1681 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1682 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001683
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001684 {
1685 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001686
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001687 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001688
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001689 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001690
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001691 // Process response from Logging service.
1692 auto respHandler = [asyncResp, entryID](
1693 const boost::system::error_code ec) {
1694 BMCWEB_LOG_DEBUG
1695 << "EventLogEntry (DBus) doDelete callback: Done";
1696 if (ec)
1697 {
1698 if (ec.value() == EBADR)
1699 {
1700 messages::resourceNotFound(asyncResp->res,
1701 "LogEntry", entryID);
1702 return;
1703 }
1704 // TODO Handle for specific error code
George Liu0fda0f12021-11-16 10:06:17 +08001705 BMCWEB_LOG_ERROR
1706 << "EventLogEntry (DBus) doDelete respHandler got error "
1707 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001708 asyncResp->res.result(
1709 boost::beast::http::status::internal_server_error);
1710 return;
1711 }
1712
1713 asyncResp->res.result(boost::beast::http::status::ok);
1714 };
1715
1716 // Make call to Logging service to request Delete Log
1717 crow::connections::systemBus->async_method_call(
1718 respHandler, "xyz.openbmc_project.Logging",
1719 "/xyz/openbmc_project/logging/entry/" + entryID,
1720 "xyz.openbmc_project.Object.Delete", "Delete");
1721 });
1722}
1723
1724inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001725{
George Liu0fda0f12021-11-16 10:06:17 +08001726 BMCWEB_ROUTE(
1727 app,
1728 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001729 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001730 .methods(boost::beast::http::verb::get)(
1731 [](const crow::Request& req,
1732 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1733 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001734
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001735 {
George Liu647b3cd2021-07-05 12:43:56 +08001736 if (!http_helpers::isOctetAccepted(
1737 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001738 {
1739 asyncResp->res.result(
1740 boost::beast::http::status::bad_request);
1741 return;
1742 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001743
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001744 std::string entryID = param;
1745 dbus::utility::escapePathForDbus(entryID);
1746
1747 crow::connections::systemBus->async_method_call(
1748 [asyncResp,
1749 entryID](const boost::system::error_code ec,
1750 const sdbusplus::message::unix_fd& unixfd) {
1751 if (ec.value() == EBADR)
1752 {
1753 messages::resourceNotFound(
1754 asyncResp->res, "EventLogAttachment", entryID);
1755 return;
1756 }
1757 if (ec)
1758 {
1759 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1760 messages::internalError(asyncResp->res);
1761 return;
1762 }
1763
1764 int fd = -1;
1765 fd = dup(unixfd);
1766 if (fd == -1)
1767 {
1768 messages::internalError(asyncResp->res);
1769 return;
1770 }
1771
1772 long long int size = lseek(fd, 0, SEEK_END);
1773 if (size == -1)
1774 {
1775 messages::internalError(asyncResp->res);
1776 return;
1777 }
1778
1779 // Arbitrary max size of 64kb
1780 constexpr int maxFileSize = 65536;
1781 if (size > maxFileSize)
1782 {
1783 BMCWEB_LOG_ERROR
1784 << "File size exceeds maximum allowed size of "
1785 << maxFileSize;
1786 messages::internalError(asyncResp->res);
1787 return;
1788 }
1789 std::vector<char> data(static_cast<size_t>(size));
1790 long long int rc = lseek(fd, 0, SEEK_SET);
1791 if (rc == -1)
1792 {
1793 messages::internalError(asyncResp->res);
1794 return;
1795 }
1796 rc = read(fd, data.data(), data.size());
1797 if ((rc == -1) || (rc != size))
1798 {
1799 messages::internalError(asyncResp->res);
1800 return;
1801 }
1802 close(fd);
1803
1804 std::string_view strData(data.data(), data.size());
1805 std::string output =
1806 crow::utility::base64encode(strData);
1807
1808 asyncResp->res.addHeader("Content-Type",
1809 "application/octet-stream");
1810 asyncResp->res.addHeader("Content-Transfer-Encoding",
1811 "Base64");
1812 asyncResp->res.body() = std::move(output);
1813 },
1814 "xyz.openbmc_project.Logging",
1815 "/xyz/openbmc_project/logging/entry/" + entryID,
1816 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1817 });
1818}
1819
Spencer Kub7028eb2021-10-26 15:27:35 +08001820constexpr const char* hostLoggerFolderPath = "/var/log/console";
1821
1822inline bool
1823 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1824 std::vector<std::filesystem::path>& hostLoggerFiles)
1825{
1826 std::error_code ec;
1827 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1828 if (ec)
1829 {
1830 BMCWEB_LOG_ERROR << ec.message();
1831 return false;
1832 }
1833 for (const std::filesystem::directory_entry& it : logPath)
1834 {
1835 std::string filename = it.path().filename();
1836 // Prefix of each log files is "log". Find the file and save the
1837 // path
1838 if (boost::starts_with(filename, "log"))
1839 {
1840 hostLoggerFiles.emplace_back(it.path());
1841 }
1842 }
1843 // As the log files rotate, they are appended with a ".#" that is higher for
1844 // the older logs. Since we start from oldest logs, sort the name in
1845 // descending order.
1846 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1847 AlphanumLess<std::string>());
1848
1849 return true;
1850}
1851
1852inline bool
1853 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
1854 uint64_t& skip, uint64_t& top,
1855 std::vector<std::string>& logEntries, size_t& logCount)
1856{
1857 GzFileReader logFile;
1858
1859 // Go though all log files and expose host logs.
1860 for (const std::filesystem::path& it : hostLoggerFiles)
1861 {
1862 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1863 {
1864 BMCWEB_LOG_ERROR << "fail to expose host logs";
1865 return false;
1866 }
1867 }
1868 // Get lastMessage from constructor by getter
1869 std::string lastMessage = logFile.getLastMessage();
1870 if (!lastMessage.empty())
1871 {
1872 logCount++;
1873 if (logCount > skip && logCount <= (skip + top))
1874 {
1875 logEntries.push_back(lastMessage);
1876 }
1877 }
1878 return true;
1879}
1880
1881inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1882 const std::string& msg,
1883 nlohmann::json& logEntryJson)
1884{
1885 // Fill in the log entry with the gathered data.
1886 logEntryJson = {
1887 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1888 {"@odata.id",
1889 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1890 logEntryID},
1891 {"Name", "Host Logger Entry"},
1892 {"Id", logEntryID},
1893 {"Message", msg},
1894 {"EntryType", "Oem"},
1895 {"Severity", "OK"},
1896 {"OemRecordFormat", "Host Logger Entry"}};
1897}
1898
1899inline void requestRoutesSystemHostLogger(App& app)
1900{
1901 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1902 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08001903 .methods(
1904 boost::beast::http::verb::
1905 get)([](const crow::Request&,
1906 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1907 asyncResp->res.jsonValue["@odata.id"] =
1908 "/redfish/v1/Systems/system/LogServices/HostLogger";
1909 asyncResp->res.jsonValue["@odata.type"] =
1910 "#LogService.v1_1_0.LogService";
1911 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1912 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1913 asyncResp->res.jsonValue["Id"] = "HostLogger";
1914 asyncResp->res.jsonValue["Entries"] = {
1915 {"@odata.id",
1916 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries"}};
1917 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001918}
1919
1920inline void requestRoutesSystemHostLoggerCollection(App& app)
1921{
1922 BMCWEB_ROUTE(app,
1923 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1924 .privileges(redfish::privileges::getLogEntry)
George Liu0fda0f12021-11-16 10:06:17 +08001925 .methods(
1926 boost::beast::http::verb::
1927 get)([](const crow::Request& req,
1928 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1929 uint64_t skip = 0;
1930 uint64_t top = maxEntriesPerPage; // Show max 1000 entries by
1931 // default, allow range 1 to
1932 // 1000 entries per page.
1933 if (!getSkipParam(asyncResp, req, skip))
1934 {
1935 return;
1936 }
1937 if (!getTopParam(asyncResp, req, top))
1938 {
1939 return;
1940 }
1941 asyncResp->res.jsonValue["@odata.id"] =
1942 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1943 asyncResp->res.jsonValue["@odata.type"] =
1944 "#LogEntryCollection.LogEntryCollection";
1945 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1946 asyncResp->res.jsonValue["Description"] =
1947 "Collection of HostLogger Entries";
1948 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1949 logEntryArray = nlohmann::json::array();
1950 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001951
George Liu0fda0f12021-11-16 10:06:17 +08001952 std::vector<std::filesystem::path> hostLoggerFiles;
1953 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1954 {
1955 BMCWEB_LOG_ERROR << "fail to get host log file path";
1956 return;
1957 }
1958
1959 size_t logCount = 0;
1960 // This vector only store the entries we want to expose that
1961 // control by skip and top.
1962 std::vector<std::string> logEntries;
1963 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1964 logCount))
1965 {
1966 messages::internalError(asyncResp->res);
1967 return;
1968 }
1969 // If vector is empty, that means skip value larger than total
1970 // log count
1971 if (logEntries.size() == 0)
1972 {
1973 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1974 return;
1975 }
1976 if (logEntries.size() > 0)
1977 {
1978 for (size_t i = 0; i < logEntries.size(); i++)
Spencer Kub7028eb2021-10-26 15:27:35 +08001979 {
George Liu0fda0f12021-11-16 10:06:17 +08001980 logEntryArray.push_back({});
1981 nlohmann::json& hostLogEntry = logEntryArray.back();
1982 fillHostLoggerEntryJson(std::to_string(skip + i),
1983 logEntries[i], hostLogEntry);
Spencer Kub7028eb2021-10-26 15:27:35 +08001984 }
1985
George Liu0fda0f12021-11-16 10:06:17 +08001986 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1987 if (skip + top < logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001988 {
George Liu0fda0f12021-11-16 10:06:17 +08001989 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1990 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
1991 std::to_string(skip + top);
Spencer Kub7028eb2021-10-26 15:27:35 +08001992 }
George Liu0fda0f12021-11-16 10:06:17 +08001993 }
1994 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001995}
1996
1997inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1998{
1999 BMCWEB_ROUTE(
2000 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
2001 .privileges(redfish::privileges::getLogEntry)
2002 .methods(boost::beast::http::verb::get)(
2003 [](const crow::Request&,
2004 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2005 const std::string& param) {
2006 const std::string& targetID = param;
2007
2008 uint64_t idInt = 0;
2009 auto [ptr, ec] = std::from_chars(
2010 targetID.data(), targetID.data() + targetID.size(), idInt);
2011 if (ec == std::errc::invalid_argument)
2012 {
2013 messages::resourceMissingAtURI(asyncResp->res, targetID);
2014 return;
2015 }
2016 if (ec == std::errc::result_out_of_range)
2017 {
2018 messages::resourceMissingAtURI(asyncResp->res, targetID);
2019 return;
2020 }
2021
2022 std::vector<std::filesystem::path> hostLoggerFiles;
2023 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2024 {
2025 BMCWEB_LOG_ERROR << "fail to get host log file path";
2026 return;
2027 }
2028
2029 size_t logCount = 0;
2030 uint64_t top = 1;
2031 std::vector<std::string> logEntries;
2032 // We can get specific entry by skip and top. For example, if we
2033 // want to get nth entry, we can set skip = n-1 and top = 1 to
2034 // get that entry
2035 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
2036 logEntries, logCount))
2037 {
2038 messages::internalError(asyncResp->res);
2039 return;
2040 }
2041
2042 if (!logEntries.empty())
2043 {
2044 fillHostLoggerEntryJson(targetID, logEntries[0],
2045 asyncResp->res.jsonValue);
2046 return;
2047 }
2048
2049 // Requested ID was not found
2050 messages::resourceMissingAtURI(asyncResp->res, targetID);
2051 });
2052}
2053
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002054inline void requestRoutesBMCLogServiceCollection(App& app)
2055{
2056 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002057 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002058 .methods(boost::beast::http::verb::get)(
2059 [](const crow::Request&,
2060 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2061 // Collections don't include the static data added by SubRoute
2062 // because it has a duplicate entry for members
2063 asyncResp->res.jsonValue["@odata.type"] =
2064 "#LogServiceCollection.LogServiceCollection";
2065 asyncResp->res.jsonValue["@odata.id"] =
2066 "/redfish/v1/Managers/bmc/LogServices";
2067 asyncResp->res.jsonValue["Name"] =
2068 "Open BMC Log Services Collection";
2069 asyncResp->res.jsonValue["Description"] =
2070 "Collection of LogServices for this Manager";
2071 nlohmann::json& logServiceArray =
2072 asyncResp->res.jsonValue["Members"];
2073 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002074#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002075 logServiceArray.push_back(
2076 {{"@odata.id",
2077 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002078#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002079#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002080 logServiceArray.push_back(
2081 {{"@odata.id",
2082 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002083#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002084 asyncResp->res.jsonValue["Members@odata.count"] =
2085 logServiceArray.size();
2086 });
2087}
Ed Tanous1da66f72018-07-27 16:13:37 -07002088
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002089inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002090{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002091 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002092 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002093 .methods(boost::beast::http::verb::get)(
2094 [](const crow::Request&,
2095 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002096
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002097 {
2098 asyncResp->res.jsonValue["@odata.type"] =
2099 "#LogService.v1_1_0.LogService";
2100 asyncResp->res.jsonValue["@odata.id"] =
2101 "/redfish/v1/Managers/bmc/LogServices/Journal";
2102 asyncResp->res.jsonValue["Name"] =
2103 "Open BMC Journal Log Service";
2104 asyncResp->res.jsonValue["Description"] =
2105 "BMC Journal Log Service";
2106 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2107 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302108
2109 std::pair<std::string, std::string> redfishDateTimeOffset =
2110 crow::utility::getDateTimeOffsetNow();
2111 asyncResp->res.jsonValue["DateTime"] =
2112 redfishDateTimeOffset.first;
2113 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2114 redfishDateTimeOffset.second;
2115
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002116 asyncResp->res.jsonValue["Entries"] = {
2117 {"@odata.id",
2118 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
2119 });
2120}
Jason M. Billse1f26342018-07-18 12:12:00 -07002121
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002122static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2123 sd_journal* journal,
2124 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002125{
2126 // Get the Log Entry contents
2127 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002128
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002129 std::string message;
2130 std::string_view syslogID;
2131 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2132 if (ret < 0)
2133 {
2134 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2135 << strerror(-ret);
2136 }
2137 if (!syslogID.empty())
2138 {
2139 message += std::string(syslogID) + ": ";
2140 }
2141
Ed Tanous39e77502019-03-04 17:35:53 -08002142 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002143 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002144 if (ret < 0)
2145 {
2146 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2147 return 1;
2148 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002149 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002150
2151 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002152 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002153 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002154 if (ret < 0)
2155 {
2156 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002157 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002158
2159 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002160 std::string entryTimeStr;
2161 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002162 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002163 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002164 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002165
2166 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002167 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002168 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002169 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2170 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002171 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002172 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002173 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002174 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002175 {"Severity", severity <= 2 ? "Critical"
2176 : severity <= 4 ? "Warning"
2177 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002178 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002179 {"Created", std::move(entryTimeStr)}};
2180 return 0;
2181}
2182
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002183inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002184{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002185 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002186 .privileges(redfish::privileges::getLogEntryCollection)
George Liu0fda0f12021-11-16 10:06:17 +08002187 .methods(
2188 boost::beast::http::verb::
2189 get)([](const crow::Request& req,
2190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2191 static constexpr const long maxEntriesPerPage = 1000;
2192 uint64_t skip = 0;
2193 uint64_t top = maxEntriesPerPage; // Show max entries by default
2194 if (!getSkipParam(asyncResp, req, skip))
2195 {
2196 return;
2197 }
2198 if (!getTopParam(asyncResp, req, top))
2199 {
2200 return;
2201 }
2202 // Collections don't include the static data added by SubRoute
2203 // because it has a duplicate entry for members
2204 asyncResp->res.jsonValue["@odata.type"] =
2205 "#LogEntryCollection.LogEntryCollection";
2206 asyncResp->res.jsonValue["@odata.id"] =
2207 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2208 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2209 asyncResp->res.jsonValue["Description"] =
2210 "Collection of BMC Journal Entries";
2211 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2212 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002213
George Liu0fda0f12021-11-16 10:06:17 +08002214 // Go through the journal and use the timestamp to create a
2215 // unique ID for each entry
2216 sd_journal* journalTmp = nullptr;
2217 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2218 if (ret < 0)
2219 {
2220 BMCWEB_LOG_ERROR << "failed to open journal: "
2221 << strerror(-ret);
2222 messages::internalError(asyncResp->res);
2223 return;
2224 }
2225 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2226 journalTmp, sd_journal_close);
2227 journalTmp = nullptr;
2228 uint64_t entryCount = 0;
2229 // Reset the unique ID on the first entry
2230 bool firstEntry = true;
2231 SD_JOURNAL_FOREACH(journal.get())
2232 {
2233 entryCount++;
2234 // Handle paging using skip (number of entries to skip from
2235 // the start) and top (number of entries to display)
2236 if (entryCount <= skip || entryCount > skip + top)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002237 {
George Liu0fda0f12021-11-16 10:06:17 +08002238 continue;
2239 }
2240
2241 std::string idStr;
2242 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2243 {
2244 continue;
2245 }
2246
2247 if (firstEntry)
2248 {
2249 firstEntry = false;
2250 }
2251
2252 logEntryArray.push_back({});
2253 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2254 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2255 bmcJournalLogEntry) != 0)
2256 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002257 messages::internalError(asyncResp->res);
2258 return;
2259 }
George Liu0fda0f12021-11-16 10:06:17 +08002260 }
2261 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2262 if (skip + top < entryCount)
2263 {
2264 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2265 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2266 std::to_string(skip + top);
2267 }
2268 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002269}
Jason M. Billse1f26342018-07-18 12:12:00 -07002270
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002271inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002272{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002273 BMCWEB_ROUTE(app,
2274 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002275 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002276 .methods(boost::beast::http::verb::get)(
2277 [](const crow::Request&,
2278 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2279 const std::string& entryID) {
2280 // Convert the unique ID back to a timestamp to find the entry
2281 uint64_t ts = 0;
2282 uint64_t index = 0;
2283 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2284 {
2285 return;
2286 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002287
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002288 sd_journal* journalTmp = nullptr;
2289 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2290 if (ret < 0)
2291 {
2292 BMCWEB_LOG_ERROR << "failed to open journal: "
2293 << strerror(-ret);
2294 messages::internalError(asyncResp->res);
2295 return;
2296 }
2297 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2298 journal(journalTmp, sd_journal_close);
2299 journalTmp = nullptr;
2300 // Go to the timestamp in the log and move to the entry at the
2301 // index tracking the unique ID
2302 std::string idStr;
2303 bool firstEntry = true;
2304 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2305 if (ret < 0)
2306 {
2307 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2308 << strerror(-ret);
2309 messages::internalError(asyncResp->res);
2310 return;
2311 }
2312 for (uint64_t i = 0; i <= index; i++)
2313 {
2314 sd_journal_next(journal.get());
2315 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2316 {
2317 messages::internalError(asyncResp->res);
2318 return;
2319 }
2320 if (firstEntry)
2321 {
2322 firstEntry = false;
2323 }
2324 }
2325 // Confirm that the entry ID matches what was requested
2326 if (idStr != entryID)
2327 {
2328 messages::resourceMissingAtURI(asyncResp->res, entryID);
2329 return;
2330 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002331
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002332 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2333 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002334 {
2335 messages::internalError(asyncResp->res);
2336 return;
2337 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002338 });
2339}
2340
2341inline void requestRoutesBMCDumpService(App& app)
2342{
2343 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002344 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08002345 .methods(
2346 boost::beast::http::verb::
2347 get)([](const crow::Request&,
2348 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2349 asyncResp->res.jsonValue["@odata.id"] =
2350 "/redfish/v1/Managers/bmc/LogServices/Dump";
2351 asyncResp->res.jsonValue["@odata.type"] =
2352 "#LogService.v1_2_0.LogService";
2353 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2354 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2355 asyncResp->res.jsonValue["Id"] = "Dump";
2356 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302357
George Liu0fda0f12021-11-16 10:06:17 +08002358 std::pair<std::string, std::string> redfishDateTimeOffset =
2359 crow::utility::getDateTimeOffsetNow();
2360 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2361 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2362 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302363
George Liu0fda0f12021-11-16 10:06:17 +08002364 asyncResp->res.jsonValue["Entries"] = {
2365 {"@odata.id",
2366 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2367 asyncResp->res.jsonValue["Actions"] = {
2368 {"#LogService.ClearLog",
2369 {{"target",
2370 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog"}}},
2371 {"#LogService.CollectDiagnosticData",
2372 {{"target",
2373 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
2374 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002375}
2376
2377inline void requestRoutesBMCDumpEntryCollection(App& app)
2378{
2379
2380 /**
2381 * Functions triggers appropriate requests on DBus
2382 */
2383 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002384 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002385 .methods(boost::beast::http::verb::get)(
2386 [](const crow::Request&,
2387 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2388 asyncResp->res.jsonValue["@odata.type"] =
2389 "#LogEntryCollection.LogEntryCollection";
2390 asyncResp->res.jsonValue["@odata.id"] =
2391 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2392 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2393 asyncResp->res.jsonValue["Description"] =
2394 "Collection of BMC Dump Entries";
2395
2396 getDumpEntryCollection(asyncResp, "BMC");
2397 });
2398}
2399
2400inline void requestRoutesBMCDumpEntry(App& app)
2401{
2402 BMCWEB_ROUTE(app,
2403 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002404 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002405 .methods(boost::beast::http::verb::get)(
2406 [](const crow::Request&,
2407 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2408 const std::string& param) {
2409 getDumpEntryById(asyncResp, param, "BMC");
2410 });
2411 BMCWEB_ROUTE(app,
2412 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002413 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002414 .methods(boost::beast::http::verb::delete_)(
2415 [](const crow::Request&,
2416 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2417 const std::string& param) {
2418 deleteDumpEntry(asyncResp, param, "bmc");
2419 });
2420}
2421
2422inline void requestRoutesBMCDumpCreate(App& app)
2423{
2424
George Liu0fda0f12021-11-16 10:06:17 +08002425 BMCWEB_ROUTE(
2426 app,
2427 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002428 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002429 .methods(boost::beast::http::verb::post)(
2430 [](const crow::Request& req,
2431 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2432 createDump(asyncResp, req, "BMC");
2433 });
2434}
2435
2436inline void requestRoutesBMCDumpClear(App& app)
2437{
George Liu0fda0f12021-11-16 10:06:17 +08002438 BMCWEB_ROUTE(
2439 app,
2440 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002441 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002442 .methods(boost::beast::http::verb::post)(
2443 [](const crow::Request&,
2444 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2445 clearDump(asyncResp, "BMC");
2446 });
2447}
2448
2449inline void requestRoutesSystemDumpService(App& app)
2450{
2451 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002452 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002453 .methods(boost::beast::http::verb::get)(
2454 [](const crow::Request&,
2455 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2456
2457 {
2458 asyncResp->res.jsonValue["@odata.id"] =
2459 "/redfish/v1/Systems/system/LogServices/Dump";
2460 asyncResp->res.jsonValue["@odata.type"] =
2461 "#LogService.v1_2_0.LogService";
2462 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2463 asyncResp->res.jsonValue["Description"] =
2464 "System Dump LogService";
2465 asyncResp->res.jsonValue["Id"] = "Dump";
2466 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302467
2468 std::pair<std::string, std::string> redfishDateTimeOffset =
2469 crow::utility::getDateTimeOffsetNow();
2470 asyncResp->res.jsonValue["DateTime"] =
2471 redfishDateTimeOffset.first;
2472 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2473 redfishDateTimeOffset.second;
2474
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002475 asyncResp->res.jsonValue["Entries"] = {
2476 {"@odata.id",
2477 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2478 asyncResp->res.jsonValue["Actions"] = {
2479 {"#LogService.ClearLog",
2480 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002481 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002482 {"#LogService.CollectDiagnosticData",
2483 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002484 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002485 });
2486}
2487
2488inline void requestRoutesSystemDumpEntryCollection(App& app)
2489{
2490
2491 /**
2492 * Functions triggers appropriate requests on DBus
2493 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002494 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002495 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002496 .methods(boost::beast::http::verb::get)(
2497 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002498 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002499 asyncResp->res.jsonValue["@odata.type"] =
2500 "#LogEntryCollection.LogEntryCollection";
2501 asyncResp->res.jsonValue["@odata.id"] =
2502 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2503 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2504 asyncResp->res.jsonValue["Description"] =
2505 "Collection of System Dump Entries";
2506
2507 getDumpEntryCollection(asyncResp, "System");
2508 });
2509}
2510
2511inline void requestRoutesSystemDumpEntry(App& app)
2512{
2513 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002514 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002515 .privileges(redfish::privileges::getLogEntry)
2516
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002517 .methods(boost::beast::http::verb::get)(
2518 [](const crow::Request&,
2519 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2520 const std::string& param) {
2521 getDumpEntryById(asyncResp, param, "System");
2522 });
2523
2524 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002525 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002526 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002527 .methods(boost::beast::http::verb::delete_)(
2528 [](const crow::Request&,
2529 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2530 const std::string& param) {
2531 deleteDumpEntry(asyncResp, param, "system");
2532 });
2533}
2534
2535inline void requestRoutesSystemDumpCreate(App& app)
2536{
George Liu0fda0f12021-11-16 10:06:17 +08002537 BMCWEB_ROUTE(
2538 app,
2539 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002540 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002541 .methods(boost::beast::http::verb::post)(
2542 [](const crow::Request& req,
2543 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2544
2545 { createDump(asyncResp, req, "System"); });
2546}
2547
2548inline void requestRoutesSystemDumpClear(App& app)
2549{
George Liu0fda0f12021-11-16 10:06:17 +08002550 BMCWEB_ROUTE(
2551 app,
2552 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002553 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002554 .methods(boost::beast::http::verb::post)(
2555 [](const crow::Request&,
2556 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2557
2558 { clearDump(asyncResp, "System"); });
2559}
2560
2561inline void requestRoutesCrashdumpService(App& app)
2562{
2563 // Note: Deviated from redfish privilege registry for GET & HEAD
2564 // method for security reasons.
2565 /**
2566 * Functions triggers appropriate requests on DBus
2567 */
2568 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002569 // This is incorrect, should be:
2570 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002571 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002572 .methods(
2573 boost::beast::http::verb::
2574 get)([](const crow::Request&,
2575 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2576 // Copy over the static data to include the entries added by
2577 // SubRoute
2578 asyncResp->res.jsonValue["@odata.id"] =
2579 "/redfish/v1/Systems/system/LogServices/Crashdump";
2580 asyncResp->res.jsonValue["@odata.type"] =
2581 "#LogService.v1_2_0.LogService";
2582 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2583 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2584 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2585 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2586 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302587
2588 std::pair<std::string, std::string> redfishDateTimeOffset =
2589 crow::utility::getDateTimeOffsetNow();
2590 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2591 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2592 redfishDateTimeOffset.second;
2593
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002594 asyncResp->res.jsonValue["Entries"] = {
2595 {"@odata.id",
2596 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2597 asyncResp->res.jsonValue["Actions"] = {
2598 {"#LogService.ClearLog",
George Liu0fda0f12021-11-16 10:06:17 +08002599 {{"target",
2600 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002601 {"#LogService.CollectDiagnosticData",
George Liu0fda0f12021-11-16 10:06:17 +08002602 {{"target",
2603 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002604 });
2605}
2606
2607void inline requestRoutesCrashdumpClear(App& app)
2608{
George Liu0fda0f12021-11-16 10:06:17 +08002609 BMCWEB_ROUTE(
2610 app,
2611 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002612 // This is incorrect, should be:
2613 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002614 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002615 .methods(boost::beast::http::verb::post)(
2616 [](const crow::Request&,
2617 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2618 crow::connections::systemBus->async_method_call(
2619 [asyncResp](const boost::system::error_code ec,
2620 const std::string&) {
2621 if (ec)
2622 {
2623 messages::internalError(asyncResp->res);
2624 return;
2625 }
2626 messages::success(asyncResp->res);
2627 },
2628 crashdumpObject, crashdumpPath, deleteAllInterface,
2629 "DeleteAll");
2630 });
2631}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002632
zhanghch058d1b46d2021-04-01 11:18:24 +08002633static void
2634 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2635 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002636{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002637 auto getStoredLogCallback =
2638 [asyncResp, logID, &logEntryJson](
2639 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002640 const std::vector<
2641 std::pair<std::string, dbus::utility::DbusVariantType>>&
2642 params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002643 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002644 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002645 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2646 if (ec.value() ==
2647 boost::system::linux_error::bad_request_descriptor)
2648 {
2649 messages::resourceNotFound(asyncResp->res, "LogEntry",
2650 logID);
2651 }
2652 else
2653 {
2654 messages::internalError(asyncResp->res);
2655 }
2656 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002657 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002658
Johnathan Mantey043a0532020-03-10 17:15:28 -07002659 std::string timestamp{};
2660 std::string filename{};
2661 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002662 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002663
2664 if (filename.empty() || timestamp.empty())
2665 {
2666 messages::resourceMissingAtURI(asyncResp->res, logID);
2667 return;
2668 }
2669
2670 std::string crashdumpURI =
2671 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2672 logID + "/" + filename;
Ed Tanousd0dbeef2021-07-01 08:46:46 -07002673 logEntryJson = {{"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002674 {"@odata.id", "/redfish/v1/Systems/system/"
2675 "LogServices/Crashdump/Entries/" +
2676 logID},
2677 {"Name", "CPU Crashdump"},
2678 {"Id", logID},
2679 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002680 {"AdditionalDataURI", std::move(crashdumpURI)},
2681 {"DiagnosticDataType", "OEM"},
2682 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002683 {"Created", std::move(timestamp)}};
2684 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002685 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002686 std::move(getStoredLogCallback), crashdumpObject,
2687 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002688 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002689}
2690
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002691inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002692{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002693 // Note: Deviated from redfish privilege registry for GET & HEAD
2694 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002695 /**
2696 * Functions triggers appropriate requests on DBus
2697 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002698 BMCWEB_ROUTE(app,
2699 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002700 // This is incorrect, should be.
2701 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002702 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002703 .methods(
2704 boost::beast::http::verb::
2705 get)([](const crow::Request&,
2706 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2707 // Collections don't include the static data added by SubRoute
2708 // because it has a duplicate entry for members
2709 auto getLogEntriesCallback = [asyncResp](
2710 const boost::system::error_code ec,
2711 const std::vector<std::string>&
2712 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002713 if (ec)
2714 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002715 if (ec.value() !=
2716 boost::system::errc::no_such_file_or_directory)
2717 {
2718 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2719 << ec.message();
2720 messages::internalError(asyncResp->res);
2721 return;
2722 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002723 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002724 asyncResp->res.jsonValue["@odata.type"] =
2725 "#LogEntryCollection.LogEntryCollection";
2726 asyncResp->res.jsonValue["@odata.id"] =
2727 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2728 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2729 asyncResp->res.jsonValue["Description"] =
2730 "Collection of Crashdump Entries";
2731 nlohmann::json& logEntryArray =
2732 asyncResp->res.jsonValue["Members"];
2733 logEntryArray = nlohmann::json::array();
2734 std::vector<std::string> logIDs;
2735 // Get the list of log entries and build up an empty array big
2736 // enough to hold them
2737 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002738 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002739 // Get the log ID
2740 std::size_t lastPos = objpath.rfind('/');
2741 if (lastPos == std::string::npos)
2742 {
2743 continue;
2744 }
2745 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002746
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002747 // Add a space for the log entry to the array
2748 logEntryArray.push_back({});
2749 }
2750 // Now go through and set up async calls to fill in the entries
2751 size_t index = 0;
2752 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002753 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002754 // Add the log entry to the array
2755 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002756 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002757 asyncResp->res.jsonValue["Members@odata.count"] =
2758 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002759 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002760 crow::connections::systemBus->async_method_call(
2761 std::move(getLogEntriesCallback),
2762 "xyz.openbmc_project.ObjectMapper",
2763 "/xyz/openbmc_project/object_mapper",
2764 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2765 std::array<const char*, 1>{crashdumpInterface});
2766 });
2767}
Ed Tanous1da66f72018-07-27 16:13:37 -07002768
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002769inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002770{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002771 // Note: Deviated from redfish privilege registry for GET & HEAD
2772 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002773
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002774 BMCWEB_ROUTE(
2775 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002776 // this is incorrect, should be
2777 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002778 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002779 .methods(boost::beast::http::verb::get)(
2780 [](const crow::Request&,
2781 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2782 const std::string& param) {
2783 const std::string& logID = param;
2784 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2785 });
2786}
Ed Tanous1da66f72018-07-27 16:13:37 -07002787
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002788inline void requestRoutesCrashdumpFile(App& app)
2789{
2790 // Note: Deviated from redfish privilege registry for GET & HEAD
2791 // method for security reasons.
2792 BMCWEB_ROUTE(
2793 app,
2794 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002795 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002796 .methods(boost::beast::http::verb::get)(
2797 [](const crow::Request&,
2798 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2799 const std::string& logID, const std::string& fileName) {
2800 auto getStoredLogCallback =
2801 [asyncResp, logID, fileName](
2802 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002803 const std::vector<std::pair<
2804 std::string, dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002805 resp) {
2806 if (ec)
2807 {
2808 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2809 << ec.message();
2810 messages::internalError(asyncResp->res);
2811 return;
2812 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002813
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002814 std::string dbusFilename{};
2815 std::string dbusTimestamp{};
2816 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002817
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002818 parseCrashdumpParameters(resp, dbusFilename,
2819 dbusTimestamp, dbusFilepath);
2820
2821 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2822 dbusFilepath.empty())
2823 {
2824 messages::resourceMissingAtURI(asyncResp->res,
2825 fileName);
2826 return;
2827 }
2828
2829 // Verify the file name parameter is correct
2830 if (fileName != dbusFilename)
2831 {
2832 messages::resourceMissingAtURI(asyncResp->res,
2833 fileName);
2834 return;
2835 }
2836
2837 if (!std::filesystem::exists(dbusFilepath))
2838 {
2839 messages::resourceMissingAtURI(asyncResp->res,
2840 fileName);
2841 return;
2842 }
2843 std::ifstream ifs(dbusFilepath, std::ios::in |
2844 std::ios::binary |
2845 std::ios::ate);
2846 std::ifstream::pos_type fileSize = ifs.tellg();
2847 if (fileSize < 0)
2848 {
2849 messages::generalError(asyncResp->res);
2850 return;
2851 }
2852 ifs.seekg(0, std::ios::beg);
2853
2854 auto crashData = std::make_unique<char[]>(
2855 static_cast<unsigned int>(fileSize));
2856
2857 ifs.read(crashData.get(), static_cast<int>(fileSize));
2858
2859 // The cast to std::string is intentional in order to
2860 // use the assign() that applies move mechanics
2861 asyncResp->res.body().assign(
2862 static_cast<std::string>(crashData.get()));
2863
2864 // Configure this to be a file download when accessed
2865 // from a browser
2866 asyncResp->res.addHeader("Content-Disposition",
2867 "attachment");
2868 };
2869 crow::connections::systemBus->async_method_call(
2870 std::move(getStoredLogCallback), crashdumpObject,
2871 crashdumpPath + std::string("/") + logID,
2872 "org.freedesktop.DBus.Properties", "GetAll",
2873 crashdumpInterface);
2874 });
2875}
2876
2877inline void requestRoutesCrashdumpCollect(App& app)
2878{
2879 // Note: Deviated from redfish privilege registry for GET & HEAD
2880 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002881 BMCWEB_ROUTE(
2882 app,
2883 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002884 // The below is incorrect; Should be ConfigureManager
2885 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002886 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002887 .methods(
2888 boost::beast::http::verb::
2889 post)([](const crow::Request& req,
2890 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2891 std::string diagnosticDataType;
2892 std::string oemDiagnosticDataType;
2893 if (!redfish::json_util::readJson(
2894 req, asyncResp->res, "DiagnosticDataType",
2895 diagnosticDataType, "OEMDiagnosticDataType",
2896 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002897 {
James Feist46229572020-02-19 15:11:58 -08002898 return;
2899 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002900
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002901 if (diagnosticDataType != "OEM")
2902 {
2903 BMCWEB_LOG_ERROR
2904 << "Only OEM DiagnosticDataType supported for Crashdump";
2905 messages::actionParameterValueFormatError(
2906 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2907 "CollectDiagnosticData");
2908 return;
2909 }
2910
Ed Tanous98be3e32021-09-16 15:05:36 -07002911 auto collectCrashdumpCallback = [asyncResp,
2912 payload(task::Payload(req))](
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002913 const boost::system::error_code
2914 ec,
Ed Tanous98be3e32021-09-16 15:05:36 -07002915 const std::string&) mutable {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002916 if (ec)
2917 {
2918 if (ec.value() ==
2919 boost::system::errc::operation_not_supported)
2920 {
2921 messages::resourceInStandby(asyncResp->res);
2922 }
2923 else if (ec.value() ==
2924 boost::system::errc::device_or_resource_busy)
2925 {
2926 messages::serviceTemporarilyUnavailable(asyncResp->res,
2927 "60");
2928 }
2929 else
2930 {
2931 messages::internalError(asyncResp->res);
2932 }
2933 return;
2934 }
George Liu0fda0f12021-11-16 10:06:17 +08002935 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2936 [](boost::system::error_code err,
2937 sdbusplus::message::message&,
2938 const std::shared_ptr<task::TaskData>& taskData) {
2939 if (!err)
2940 {
2941 taskData->messages.emplace_back(
2942 messages::taskCompletedOK(
2943 std::to_string(taskData->index)));
2944 taskData->state = "Completed";
2945 }
2946 return task::completed;
2947 },
2948 "type='signal',interface='org.freedesktop.DBus."
2949 "Properties',"
2950 "member='PropertiesChanged',arg0namespace='com.intel.crashdump'");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002951 task->startTimer(std::chrono::minutes(5));
2952 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -07002953 task->payload.emplace(std::move(payload));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002954 };
2955
2956 if (oemDiagnosticDataType == "OnDemand")
2957 {
2958 crow::connections::systemBus->async_method_call(
2959 std::move(collectCrashdumpCallback), crashdumpObject,
2960 crashdumpPath, crashdumpOnDemandInterface,
2961 "GenerateOnDemandLog");
2962 }
2963 else if (oemDiagnosticDataType == "Telemetry")
2964 {
2965 crow::connections::systemBus->async_method_call(
2966 std::move(collectCrashdumpCallback), crashdumpObject,
2967 crashdumpPath, crashdumpTelemetryInterface,
2968 "GenerateTelemetryLog");
2969 }
2970 else
2971 {
2972 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2973 << oemDiagnosticDataType;
2974 messages::actionParameterValueFormatError(
2975 asyncResp->res, oemDiagnosticDataType,
2976 "OEMDiagnosticDataType", "CollectDiagnosticData");
2977 return;
2978 }
2979 });
2980}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002981
Andrew Geisslercb92c032018-08-17 07:56:14 -07002982/**
2983 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2984 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002985inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002986{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002987 /**
2988 * Function handles POST method request.
2989 * The Clear Log actions does not require any parameter.The action deletes
2990 * all entries found in the Entries collection for this Log Service.
2991 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002992
George Liu0fda0f12021-11-16 10:06:17 +08002993 BMCWEB_ROUTE(
2994 app,
2995 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002996 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002997 .methods(boost::beast::http::verb::post)(
2998 [](const crow::Request&,
2999 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3000 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003001
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003002 // Process response from Logging service.
3003 auto respHandler = [asyncResp](
3004 const boost::system::error_code ec) {
3005 BMCWEB_LOG_DEBUG
3006 << "doClearLog resp_handler callback: Done";
3007 if (ec)
3008 {
3009 // TODO Handle for specific error code
3010 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
3011 << ec;
3012 asyncResp->res.result(
3013 boost::beast::http::status::internal_server_error);
3014 return;
3015 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003016
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003017 asyncResp->res.result(
3018 boost::beast::http::status::no_content);
3019 };
3020
3021 // Make call to Logging service to request Clear Log
3022 crow::connections::systemBus->async_method_call(
3023 respHandler, "xyz.openbmc_project.Logging",
3024 "/xyz/openbmc_project/logging",
3025 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3026 });
3027}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003028
3029/****************************************************
3030 * Redfish PostCode interfaces
3031 * using DBUS interface: getPostCodesTS
3032 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003033inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003034{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003035 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003036 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08003037 .methods(
3038 boost::beast::http::verb::
3039 get)([](const crow::Request&,
3040 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3041 asyncResp->res.jsonValue = {
3042 {"@odata.id",
3043 "/redfish/v1/Systems/system/LogServices/PostCodes"},
3044 {"@odata.type", "#LogService.v1_1_0.LogService"},
3045 {"Name", "POST Code Log Service"},
3046 {"Description", "POST Code Log Service"},
3047 {"Id", "BIOS POST Code Log"},
3048 {"OverWritePolicy", "WrapsWhenFull"},
3049 {"Entries",
3050 {{"@odata.id",
3051 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05303052
George Liu0fda0f12021-11-16 10:06:17 +08003053 std::pair<std::string, std::string> redfishDateTimeOffset =
3054 crow::utility::getDateTimeOffsetNow();
3055 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3056 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3057 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303058
George Liu0fda0f12021-11-16 10:06:17 +08003059 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3060 {"target",
3061 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3062 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003063}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003064
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003065inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003066{
George Liu0fda0f12021-11-16 10:06:17 +08003067 BMCWEB_ROUTE(
3068 app,
3069 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003070 // The following privilege is incorrect; It should be ConfigureManager
3071 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003072 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003073 .methods(boost::beast::http::verb::post)(
3074 [](const crow::Request&,
3075 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3076 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003077
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003078 // Make call to post-code service to request clear all
3079 crow::connections::systemBus->async_method_call(
3080 [asyncResp](const boost::system::error_code ec) {
3081 if (ec)
3082 {
3083 // TODO Handle for specific error code
3084 BMCWEB_LOG_ERROR
3085 << "doClearPostCodes resp_handler got error "
3086 << ec;
3087 asyncResp->res.result(boost::beast::http::status::
3088 internal_server_error);
3089 messages::internalError(asyncResp->res);
3090 return;
3091 }
3092 },
3093 "xyz.openbmc_project.State.Boot.PostCode0",
3094 "/xyz/openbmc_project/State/Boot/PostCode0",
3095 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3096 });
3097}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003098
3099static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003100 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303101 const boost::container::flat_map<
3102 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003103 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3104 const uint64_t skip = 0, const uint64_t top = 0)
3105{
3106 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003107 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05303108 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003109
3110 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003111 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003112
3113 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303114 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3115 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003116 {
3117 currentCodeIndex++;
3118 std::string postcodeEntryID =
3119 "B" + std::to_string(bootIndex) + "-" +
3120 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3121
3122 uint64_t usecSinceEpoch = code.first;
3123 uint64_t usTimeOffset = 0;
3124
3125 if (1 == currentCodeIndex)
3126 { // already incremented
3127 firstCodeTimeUs = code.first;
3128 }
3129 else
3130 {
3131 usTimeOffset = code.first - firstCodeTimeUs;
3132 }
3133
3134 // skip if no specific codeIndex is specified and currentCodeIndex does
3135 // not fall between top and skip
3136 if ((codeIndex == 0) &&
3137 (currentCodeIndex <= skip || currentCodeIndex > top))
3138 {
3139 continue;
3140 }
3141
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003142 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003143 // currentIndex
3144 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3145 {
3146 // This is done for simplicity. 1st entry is needed to calculate
3147 // time offset. To improve efficiency, one can get to the entry
3148 // directly (possibly with flatmap's nth method)
3149 continue;
3150 }
3151
3152 // currentCodeIndex is within top and skip or equal to specified code
3153 // index
3154
3155 // Get the Created time from the timestamp
3156 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003157 entryTimeStr =
3158 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003159
3160 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3161 std::ostringstream hexCode;
3162 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303163 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003164 std::ostringstream timeOffsetStr;
3165 // Set Fixed -Point Notation
3166 timeOffsetStr << std::fixed;
3167 // Set precision to 4 digits
3168 timeOffsetStr << std::setprecision(4);
3169 // Add double to stream
3170 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3171 std::vector<std::string> messageArgs = {
3172 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3173
3174 // Get MessageArgs template from message registry
3175 std::string msg;
3176 if (message != nullptr)
3177 {
3178 msg = message->message;
3179
3180 // fill in this post code value
3181 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003182 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003183 {
3184 std::string argStr = "%" + std::to_string(++i);
3185 size_t argPos = msg.find(argStr);
3186 if (argPos != std::string::npos)
3187 {
3188 msg.replace(argPos, argStr.length(), messageArg);
3189 }
3190 }
3191 }
3192
Tim Leed4342a92020-04-27 11:47:58 +08003193 // Get Severity template from message registry
3194 std::string severity;
3195 if (message != nullptr)
3196 {
3197 severity = message->severity;
3198 }
3199
ZhikuiRena3316fc2020-01-29 14:58:08 -08003200 // add to AsyncResp
3201 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003202 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003203 bmcLogEntry = {
3204 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3205 {"@odata.id",
3206 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3207 postcodeEntryID},
3208 {"Name", "POST Code Log Entry"},
3209 {"Id", postcodeEntryID},
3210 {"Message", std::move(msg)},
3211 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3212 {"MessageArgs", std::move(messageArgs)},
3213 {"EntryType", "Event"},
3214 {"Severity", std::move(severity)},
3215 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003216 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3217 {
3218 bmcLogEntry["AdditionalDataURI"] =
3219 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3220 postcodeEntryID + "/attachment";
3221 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003222 }
3223}
3224
zhanghch058d1b46d2021-04-01 11:18:24 +08003225static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003226 const uint16_t bootIndex,
3227 const uint64_t codeIndex)
3228{
3229 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303230 [aResp, bootIndex,
3231 codeIndex](const boost::system::error_code ec,
3232 const boost::container::flat_map<
3233 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3234 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003235 if (ec)
3236 {
3237 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3238 messages::internalError(aResp->res);
3239 return;
3240 }
3241
3242 // skip the empty postcode boots
3243 if (postcode.empty())
3244 {
3245 return;
3246 }
3247
3248 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3249
3250 aResp->res.jsonValue["Members@odata.count"] =
3251 aResp->res.jsonValue["Members"].size();
3252 },
Jonathan Doman15124762021-01-07 17:54:17 -08003253 "xyz.openbmc_project.State.Boot.PostCode0",
3254 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003255 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3256 bootIndex);
3257}
3258
zhanghch058d1b46d2021-04-01 11:18:24 +08003259static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003260 const uint16_t bootIndex,
3261 const uint16_t bootCount,
3262 const uint64_t entryCount, const uint64_t skip,
3263 const uint64_t top)
3264{
3265 crow::connections::systemBus->async_method_call(
3266 [aResp, bootIndex, bootCount, entryCount, skip,
3267 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303268 const boost::container::flat_map<
3269 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3270 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003271 if (ec)
3272 {
3273 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3274 messages::internalError(aResp->res);
3275 return;
3276 }
3277
3278 uint64_t endCount = entryCount;
3279 if (!postcode.empty())
3280 {
3281 endCount = entryCount + postcode.size();
3282
3283 if ((skip < endCount) && ((top + skip) > entryCount))
3284 {
3285 uint64_t thisBootSkip =
3286 std::max(skip, entryCount) - entryCount;
3287 uint64_t thisBootTop =
3288 std::min(top + skip, endCount) - entryCount;
3289
3290 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3291 thisBootSkip, thisBootTop);
3292 }
3293 aResp->res.jsonValue["Members@odata.count"] = endCount;
3294 }
3295
3296 // continue to previous bootIndex
3297 if (bootIndex < bootCount)
3298 {
3299 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3300 bootCount, endCount, skip, top);
3301 }
3302 else
3303 {
3304 aResp->res.jsonValue["Members@odata.nextLink"] =
George Liu0fda0f12021-11-16 10:06:17 +08003305 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
ZhikuiRena3316fc2020-01-29 14:58:08 -08003306 std::to_string(skip + top);
3307 }
3308 },
Jonathan Doman15124762021-01-07 17:54:17 -08003309 "xyz.openbmc_project.State.Boot.PostCode0",
3310 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003311 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3312 bootIndex);
3313}
3314
zhanghch058d1b46d2021-04-01 11:18:24 +08003315static void
3316 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3317 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003318{
3319 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003320 sdbusplus::asio::getProperty<uint16_t>(
3321 *crow::connections::systemBus,
3322 "xyz.openbmc_project.State.Boot.PostCode0",
3323 "/xyz/openbmc_project/State/Boot/PostCode0",
3324 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3325 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3326 const uint16_t bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003327 if (ec)
3328 {
3329 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3330 messages::internalError(aResp->res);
3331 return;
3332 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003333 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3334 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003335}
3336
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003337inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003338{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003339 BMCWEB_ROUTE(app,
3340 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003341 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003342 .methods(boost::beast::http::verb::get)(
3343 [](const crow::Request& req,
3344 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3345 asyncResp->res.jsonValue["@odata.type"] =
3346 "#LogEntryCollection.LogEntryCollection";
3347 asyncResp->res.jsonValue["@odata.id"] =
3348 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3349 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3350 asyncResp->res.jsonValue["Description"] =
3351 "Collection of POST Code Log Entries";
3352 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3353 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003354
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003355 uint64_t skip = 0;
3356 uint64_t top = maxEntriesPerPage; // Show max entries by default
3357 if (!getSkipParam(asyncResp, req, skip))
3358 {
3359 return;
3360 }
3361 if (!getTopParam(asyncResp, req, top))
3362 {
3363 return;
3364 }
3365 getCurrentBootNumber(asyncResp, skip, top);
3366 });
3367}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003368
George Liu647b3cd2021-07-05 12:43:56 +08003369/**
3370 * @brief Parse post code ID and get the current value and index value
3371 * eg: postCodeID=B1-2, currentValue=1, index=2
3372 *
3373 * @param[in] postCodeID Post Code ID
3374 * @param[out] currentValue Current value
3375 * @param[out] index Index value
3376 *
3377 * @return bool true if the parsing is successful, false the parsing fails
3378 */
3379inline static bool parsePostCode(const std::string& postCodeID,
3380 uint64_t& currentValue, uint16_t& index)
3381{
3382 std::vector<std::string> split;
3383 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3384 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3385 {
3386 return false;
3387 }
3388
3389 const char* start = split[0].data() + 1;
3390 const char* end = split[0].data() + split[0].size();
3391 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3392
3393 if (ptrIndex != end || ecIndex != std::errc())
3394 {
3395 return false;
3396 }
3397
3398 start = split[1].data();
3399 end = split[1].data() + split[1].size();
3400 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3401 if (ptrValue != end || ecValue != std::errc())
3402 {
3403 return false;
3404 }
3405
3406 return true;
3407}
3408
3409inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3410{
George Liu0fda0f12021-11-16 10:06:17 +08003411 BMCWEB_ROUTE(
3412 app,
3413 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003414 .privileges(redfish::privileges::getLogEntry)
3415 .methods(boost::beast::http::verb::get)(
3416 [](const crow::Request& req,
3417 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3418 const std::string& postCodeID) {
3419 if (!http_helpers::isOctetAccepted(
3420 req.getHeaderValue("Accept")))
3421 {
3422 asyncResp->res.result(
3423 boost::beast::http::status::bad_request);
3424 return;
3425 }
3426
3427 uint64_t currentValue = 0;
3428 uint16_t index = 0;
3429 if (!parsePostCode(postCodeID, currentValue, index))
3430 {
3431 messages::resourceNotFound(asyncResp->res, "LogEntry",
3432 postCodeID);
3433 return;
3434 }
3435
3436 crow::connections::systemBus->async_method_call(
3437 [asyncResp, postCodeID, currentValue](
3438 const boost::system::error_code ec,
3439 const std::vector<std::tuple<
3440 uint64_t, std::vector<uint8_t>>>& postcodes) {
3441 if (ec.value() == EBADR)
3442 {
3443 messages::resourceNotFound(asyncResp->res,
3444 "LogEntry", postCodeID);
3445 return;
3446 }
3447 if (ec)
3448 {
3449 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3450 messages::internalError(asyncResp->res);
3451 return;
3452 }
3453
3454 size_t value = static_cast<size_t>(currentValue) - 1;
3455 if (value == std::string::npos ||
3456 postcodes.size() < currentValue)
3457 {
3458 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3459 messages::resourceNotFound(asyncResp->res,
3460 "LogEntry", postCodeID);
3461 return;
3462 }
3463
3464 auto& [tID, code] = postcodes[value];
3465 if (code.empty())
3466 {
3467 BMCWEB_LOG_INFO << "No found post code data";
3468 messages::resourceNotFound(asyncResp->res,
3469 "LogEntry", postCodeID);
3470 return;
3471 }
3472
3473 std::string_view strData(
3474 reinterpret_cast<const char*>(code.data()),
3475 code.size());
3476
3477 asyncResp->res.addHeader("Content-Type",
3478 "application/octet-stream");
3479 asyncResp->res.addHeader("Content-Transfer-Encoding",
3480 "Base64");
3481 asyncResp->res.body() =
3482 crow::utility::base64encode(strData);
3483 },
3484 "xyz.openbmc_project.State.Boot.PostCode0",
3485 "/xyz/openbmc_project/State/Boot/PostCode0",
3486 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3487 index);
3488 });
3489}
3490
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003491inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003492{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003493 BMCWEB_ROUTE(
3494 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003495 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003496 .methods(boost::beast::http::verb::get)(
3497 [](const crow::Request&,
3498 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3499 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003500 uint16_t bootIndex = 0;
3501 uint64_t codeIndex = 0;
3502 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003503 {
3504 // Requested ID was not found
3505 messages::resourceMissingAtURI(asyncResp->res, targetID);
3506 return;
3507 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003508 if (bootIndex == 0 || codeIndex == 0)
3509 {
3510 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3511 << targetID;
3512 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003513
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003514 asyncResp->res.jsonValue["@odata.type"] =
3515 "#LogEntry.v1_4_0.LogEntry";
3516 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08003517 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003518 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3519 asyncResp->res.jsonValue["Description"] =
3520 "Collection of POST Code Log Entries";
3521 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3522 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003523
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003524 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3525 });
3526}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003527
Ed Tanous1da66f72018-07-27 16:13:37 -07003528} // namespace redfish