blob: ddd124bb496d7164a8da6dfc8370a5803bff939b [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 Tanous914e2d52022-01-07 11:38:34 -08001366 const 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 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001381 const uint32_t* id = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001382 std::time_t timestamp{};
1383 std::time_t updateTimestamp{};
Ed Tanous914e2d52022-01-07 11:38:34 -08001384 const std::string* severity = nullptr;
1385 const std::string* message = nullptr;
1386 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001387 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 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001437 const bool* resolveptr =
1438 std::get_if<bool>(
1439 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001440 if (resolveptr == nullptr)
1441 {
1442 messages::internalError(
1443 asyncResp->res);
1444 return;
1445 }
1446 resolved = *resolveptr;
1447 }
1448 }
1449 if (id == nullptr || message == nullptr ||
1450 severity == nullptr)
1451 {
1452 messages::internalError(asyncResp->res);
1453 return;
1454 }
1455 }
1456 else if (interfaceMap.first ==
1457 "xyz.openbmc_project.Common.FilePath")
1458 {
1459 for (auto& propertyMap : interfaceMap.second)
1460 {
1461 if (propertyMap.first == "Path")
1462 {
1463 filePath = std::get_if<std::string>(
1464 &propertyMap.second);
1465 }
1466 }
1467 }
1468 }
1469 // Object path without the
1470 // xyz.openbmc_project.Logging.Entry interface, ignore
1471 // and continue.
1472 if (id == nullptr || message == nullptr ||
1473 severity == nullptr)
1474 {
1475 continue;
1476 }
1477 entriesArray.push_back({});
1478 nlohmann::json& thisEntry = entriesArray.back();
1479 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1480 thisEntry["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001481 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001482 std::to_string(*id);
1483 thisEntry["Name"] = "System Event Log Entry";
1484 thisEntry["Id"] = std::to_string(*id);
1485 thisEntry["Message"] = *message;
1486 thisEntry["Resolved"] = resolved;
1487 thisEntry["EntryType"] = "Event";
1488 thisEntry["Severity"] =
1489 translateSeverityDbusToRedfish(*severity);
1490 thisEntry["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001491 crow::utility::getDateTimeStdtime(timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001492 thisEntry["Modified"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001493 crow::utility::getDateTimeStdtime(updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001494 if (filePath != nullptr)
1495 {
1496 thisEntry["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001497 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001498 std::to_string(*id) + "/attachment";
1499 }
1500 }
1501 std::sort(entriesArray.begin(), entriesArray.end(),
1502 [](const nlohmann::json& left,
1503 const nlohmann::json& right) {
1504 return (left["Id"] <= right["Id"]);
1505 });
1506 asyncResp->res.jsonValue["Members@odata.count"] =
1507 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001508 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001509 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1510 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1511 });
1512}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001513
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001514inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001515{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001516 BMCWEB_ROUTE(
1517 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001518 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001519 .methods(boost::beast::http::verb::get)(
1520 [](const crow::Request&,
1521 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1522 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001523
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001524 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001525 std::string entryID = param;
1526 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001527
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001528 // DBus implementation of EventLog/Entries
1529 // Make call to Logging Service to find all log entry objects
1530 crow::connections::systemBus->async_method_call(
1531 [asyncResp, entryID](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001532 const GetManagedPropertyType& resp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001533 if (ec.value() == EBADR)
1534 {
1535 messages::resourceNotFound(
1536 asyncResp->res, "EventLogEntry", entryID);
1537 return;
1538 }
1539 if (ec)
1540 {
George Liu0fda0f12021-11-16 10:06:17 +08001541 BMCWEB_LOG_ERROR
1542 << "EventLogEntry (DBus) resp_handler got error "
1543 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001544 messages::internalError(asyncResp->res);
1545 return;
1546 }
Ed Tanous914e2d52022-01-07 11:38:34 -08001547 const uint32_t* id = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001548 std::time_t timestamp{};
1549 std::time_t updateTimestamp{};
Ed Tanous914e2d52022-01-07 11:38:34 -08001550 const std::string* severity = nullptr;
1551 const std::string* message = nullptr;
1552 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001553 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001554
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001555 for (auto& propertyMap : resp)
1556 {
1557 if (propertyMap.first == "Id")
1558 {
1559 id = std::get_if<uint32_t>(&propertyMap.second);
1560 }
1561 else if (propertyMap.first == "Timestamp")
1562 {
1563 const uint64_t* millisTimeStamp =
1564 std::get_if<uint64_t>(&propertyMap.second);
1565 if (millisTimeStamp != nullptr)
1566 {
1567 timestamp = crow::utility::getTimestamp(
1568 *millisTimeStamp);
1569 }
1570 }
1571 else if (propertyMap.first == "UpdateTimestamp")
1572 {
1573 const uint64_t* millisTimeStamp =
1574 std::get_if<uint64_t>(&propertyMap.second);
1575 if (millisTimeStamp != nullptr)
1576 {
1577 updateTimestamp =
1578 crow::utility::getTimestamp(
1579 *millisTimeStamp);
1580 }
1581 }
1582 else if (propertyMap.first == "Severity")
1583 {
1584 severity = std::get_if<std::string>(
1585 &propertyMap.second);
1586 }
1587 else if (propertyMap.first == "Message")
1588 {
1589 message = std::get_if<std::string>(
1590 &propertyMap.second);
1591 }
1592 else if (propertyMap.first == "Resolved")
1593 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001594 const bool* resolveptr =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001595 std::get_if<bool>(&propertyMap.second);
1596 if (resolveptr == nullptr)
1597 {
1598 messages::internalError(asyncResp->res);
1599 return;
1600 }
1601 resolved = *resolveptr;
1602 }
1603 else if (propertyMap.first == "Path")
1604 {
1605 filePath = std::get_if<std::string>(
1606 &propertyMap.second);
1607 }
1608 }
1609 if (id == nullptr || message == nullptr ||
1610 severity == nullptr)
1611 {
1612 messages::internalError(asyncResp->res);
1613 return;
1614 }
1615 asyncResp->res.jsonValue["@odata.type"] =
1616 "#LogEntry.v1_8_0.LogEntry";
1617 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001618 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001619 std::to_string(*id);
1620 asyncResp->res.jsonValue["Name"] =
1621 "System Event Log Entry";
1622 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1623 asyncResp->res.jsonValue["Message"] = *message;
1624 asyncResp->res.jsonValue["Resolved"] = resolved;
1625 asyncResp->res.jsonValue["EntryType"] = "Event";
1626 asyncResp->res.jsonValue["Severity"] =
1627 translateSeverityDbusToRedfish(*severity);
1628 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001629 crow::utility::getDateTimeStdtime(timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001630 asyncResp->res.jsonValue["Modified"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001631 crow::utility::getDateTimeStdtime(updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001632 if (filePath != nullptr)
1633 {
1634 asyncResp->res.jsonValue["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001635 "/redfish/v1/Systems/system/LogServices/EventLog/attachment/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001636 std::to_string(*id);
1637 }
1638 },
1639 "xyz.openbmc_project.Logging",
1640 "/xyz/openbmc_project/logging/entry/" + entryID,
1641 "org.freedesktop.DBus.Properties", "GetAll", "");
1642 });
1643
1644 BMCWEB_ROUTE(
1645 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001646 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001647 .methods(boost::beast::http::verb::patch)(
1648 [](const crow::Request& req,
1649 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1650 const std::string& entryId) {
1651 std::optional<bool> resolved;
1652
1653 if (!json_util::readJson(req, asyncResp->res, "Resolved",
1654 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001655 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001656 return;
1657 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001658 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001659
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001661 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001662 if (ec)
1663 {
1664 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1665 messages::internalError(asyncResp->res);
1666 return;
1667 }
1668 },
1669 "xyz.openbmc_project.Logging",
1670 "/xyz/openbmc_project/logging/entry/" + entryId,
1671 "org.freedesktop.DBus.Properties", "Set",
1672 "xyz.openbmc_project.Logging.Entry", "Resolved",
Ed Tanous168e20c2021-12-13 14:39:53 -08001673 dbus::utility::DbusVariantType(*resolved));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001674 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001675
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001676 BMCWEB_ROUTE(
1677 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001678 .privileges(redfish::privileges::deleteLogEntry)
1679
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001680 .methods(boost::beast::http::verb::delete_)(
1681 [](const crow::Request&,
1682 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1683 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001684
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001685 {
1686 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001687
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001688 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001689
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001690 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001691
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001692 // Process response from Logging service.
1693 auto respHandler = [asyncResp, entryID](
1694 const boost::system::error_code ec) {
1695 BMCWEB_LOG_DEBUG
1696 << "EventLogEntry (DBus) doDelete callback: Done";
1697 if (ec)
1698 {
1699 if (ec.value() == EBADR)
1700 {
1701 messages::resourceNotFound(asyncResp->res,
1702 "LogEntry", entryID);
1703 return;
1704 }
1705 // TODO Handle for specific error code
George Liu0fda0f12021-11-16 10:06:17 +08001706 BMCWEB_LOG_ERROR
1707 << "EventLogEntry (DBus) doDelete respHandler got error "
1708 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001709 asyncResp->res.result(
1710 boost::beast::http::status::internal_server_error);
1711 return;
1712 }
1713
1714 asyncResp->res.result(boost::beast::http::status::ok);
1715 };
1716
1717 // Make call to Logging service to request Delete Log
1718 crow::connections::systemBus->async_method_call(
1719 respHandler, "xyz.openbmc_project.Logging",
1720 "/xyz/openbmc_project/logging/entry/" + entryID,
1721 "xyz.openbmc_project.Object.Delete", "Delete");
1722 });
1723}
1724
1725inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001726{
George Liu0fda0f12021-11-16 10:06:17 +08001727 BMCWEB_ROUTE(
1728 app,
1729 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001730 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001731 .methods(boost::beast::http::verb::get)(
1732 [](const crow::Request& req,
1733 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1734 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001735
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001736 {
George Liu647b3cd2021-07-05 12:43:56 +08001737 if (!http_helpers::isOctetAccepted(
1738 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001739 {
1740 asyncResp->res.result(
1741 boost::beast::http::status::bad_request);
1742 return;
1743 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001744
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001745 std::string entryID = param;
1746 dbus::utility::escapePathForDbus(entryID);
1747
1748 crow::connections::systemBus->async_method_call(
1749 [asyncResp,
1750 entryID](const boost::system::error_code ec,
1751 const sdbusplus::message::unix_fd& unixfd) {
1752 if (ec.value() == EBADR)
1753 {
1754 messages::resourceNotFound(
1755 asyncResp->res, "EventLogAttachment", entryID);
1756 return;
1757 }
1758 if (ec)
1759 {
1760 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1761 messages::internalError(asyncResp->res);
1762 return;
1763 }
1764
1765 int fd = -1;
1766 fd = dup(unixfd);
1767 if (fd == -1)
1768 {
1769 messages::internalError(asyncResp->res);
1770 return;
1771 }
1772
1773 long long int size = lseek(fd, 0, SEEK_END);
1774 if (size == -1)
1775 {
1776 messages::internalError(asyncResp->res);
1777 return;
1778 }
1779
1780 // Arbitrary max size of 64kb
1781 constexpr int maxFileSize = 65536;
1782 if (size > maxFileSize)
1783 {
1784 BMCWEB_LOG_ERROR
1785 << "File size exceeds maximum allowed size of "
1786 << maxFileSize;
1787 messages::internalError(asyncResp->res);
1788 return;
1789 }
1790 std::vector<char> data(static_cast<size_t>(size));
1791 long long int rc = lseek(fd, 0, SEEK_SET);
1792 if (rc == -1)
1793 {
1794 messages::internalError(asyncResp->res);
1795 return;
1796 }
1797 rc = read(fd, data.data(), data.size());
1798 if ((rc == -1) || (rc != size))
1799 {
1800 messages::internalError(asyncResp->res);
1801 return;
1802 }
1803 close(fd);
1804
1805 std::string_view strData(data.data(), data.size());
1806 std::string output =
1807 crow::utility::base64encode(strData);
1808
1809 asyncResp->res.addHeader("Content-Type",
1810 "application/octet-stream");
1811 asyncResp->res.addHeader("Content-Transfer-Encoding",
1812 "Base64");
1813 asyncResp->res.body() = std::move(output);
1814 },
1815 "xyz.openbmc_project.Logging",
1816 "/xyz/openbmc_project/logging/entry/" + entryID,
1817 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1818 });
1819}
1820
Spencer Kub7028eb2021-10-26 15:27:35 +08001821constexpr const char* hostLoggerFolderPath = "/var/log/console";
1822
1823inline bool
1824 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1825 std::vector<std::filesystem::path>& hostLoggerFiles)
1826{
1827 std::error_code ec;
1828 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1829 if (ec)
1830 {
1831 BMCWEB_LOG_ERROR << ec.message();
1832 return false;
1833 }
1834 for (const std::filesystem::directory_entry& it : logPath)
1835 {
1836 std::string filename = it.path().filename();
1837 // Prefix of each log files is "log". Find the file and save the
1838 // path
1839 if (boost::starts_with(filename, "log"))
1840 {
1841 hostLoggerFiles.emplace_back(it.path());
1842 }
1843 }
1844 // As the log files rotate, they are appended with a ".#" that is higher for
1845 // the older logs. Since we start from oldest logs, sort the name in
1846 // descending order.
1847 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1848 AlphanumLess<std::string>());
1849
1850 return true;
1851}
1852
1853inline bool
1854 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
1855 uint64_t& skip, uint64_t& top,
1856 std::vector<std::string>& logEntries, size_t& logCount)
1857{
1858 GzFileReader logFile;
1859
1860 // Go though all log files and expose host logs.
1861 for (const std::filesystem::path& it : hostLoggerFiles)
1862 {
1863 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1864 {
1865 BMCWEB_LOG_ERROR << "fail to expose host logs";
1866 return false;
1867 }
1868 }
1869 // Get lastMessage from constructor by getter
1870 std::string lastMessage = logFile.getLastMessage();
1871 if (!lastMessage.empty())
1872 {
1873 logCount++;
1874 if (logCount > skip && logCount <= (skip + top))
1875 {
1876 logEntries.push_back(lastMessage);
1877 }
1878 }
1879 return true;
1880}
1881
1882inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1883 const std::string& msg,
1884 nlohmann::json& logEntryJson)
1885{
1886 // Fill in the log entry with the gathered data.
1887 logEntryJson = {
1888 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1889 {"@odata.id",
1890 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1891 logEntryID},
1892 {"Name", "Host Logger Entry"},
1893 {"Id", logEntryID},
1894 {"Message", msg},
1895 {"EntryType", "Oem"},
1896 {"Severity", "OK"},
1897 {"OemRecordFormat", "Host Logger Entry"}};
1898}
1899
1900inline void requestRoutesSystemHostLogger(App& app)
1901{
1902 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1903 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08001904 .methods(
1905 boost::beast::http::verb::
1906 get)([](const crow::Request&,
1907 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1908 asyncResp->res.jsonValue["@odata.id"] =
1909 "/redfish/v1/Systems/system/LogServices/HostLogger";
1910 asyncResp->res.jsonValue["@odata.type"] =
1911 "#LogService.v1_1_0.LogService";
1912 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1913 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1914 asyncResp->res.jsonValue["Id"] = "HostLogger";
1915 asyncResp->res.jsonValue["Entries"] = {
1916 {"@odata.id",
1917 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries"}};
1918 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001919}
1920
1921inline void requestRoutesSystemHostLoggerCollection(App& app)
1922{
1923 BMCWEB_ROUTE(app,
1924 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1925 .privileges(redfish::privileges::getLogEntry)
George Liu0fda0f12021-11-16 10:06:17 +08001926 .methods(
1927 boost::beast::http::verb::
1928 get)([](const crow::Request& req,
1929 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1930 uint64_t skip = 0;
1931 uint64_t top = maxEntriesPerPage; // Show max 1000 entries by
1932 // default, allow range 1 to
1933 // 1000 entries per page.
1934 if (!getSkipParam(asyncResp, req, skip))
1935 {
1936 return;
1937 }
1938 if (!getTopParam(asyncResp, req, top))
1939 {
1940 return;
1941 }
1942 asyncResp->res.jsonValue["@odata.id"] =
1943 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1944 asyncResp->res.jsonValue["@odata.type"] =
1945 "#LogEntryCollection.LogEntryCollection";
1946 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1947 asyncResp->res.jsonValue["Description"] =
1948 "Collection of HostLogger Entries";
1949 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1950 logEntryArray = nlohmann::json::array();
1951 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001952
George Liu0fda0f12021-11-16 10:06:17 +08001953 std::vector<std::filesystem::path> hostLoggerFiles;
1954 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1955 {
1956 BMCWEB_LOG_ERROR << "fail to get host log file path";
1957 return;
1958 }
1959
1960 size_t logCount = 0;
1961 // This vector only store the entries we want to expose that
1962 // control by skip and top.
1963 std::vector<std::string> logEntries;
1964 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1965 logCount))
1966 {
1967 messages::internalError(asyncResp->res);
1968 return;
1969 }
1970 // If vector is empty, that means skip value larger than total
1971 // log count
1972 if (logEntries.size() == 0)
1973 {
1974 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1975 return;
1976 }
1977 if (logEntries.size() > 0)
1978 {
1979 for (size_t i = 0; i < logEntries.size(); i++)
Spencer Kub7028eb2021-10-26 15:27:35 +08001980 {
George Liu0fda0f12021-11-16 10:06:17 +08001981 logEntryArray.push_back({});
1982 nlohmann::json& hostLogEntry = logEntryArray.back();
1983 fillHostLoggerEntryJson(std::to_string(skip + i),
1984 logEntries[i], hostLogEntry);
Spencer Kub7028eb2021-10-26 15:27:35 +08001985 }
1986
George Liu0fda0f12021-11-16 10:06:17 +08001987 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1988 if (skip + top < logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001989 {
George Liu0fda0f12021-11-16 10:06:17 +08001990 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1991 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
1992 std::to_string(skip + top);
Spencer Kub7028eb2021-10-26 15:27:35 +08001993 }
George Liu0fda0f12021-11-16 10:06:17 +08001994 }
1995 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001996}
1997
1998inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1999{
2000 BMCWEB_ROUTE(
2001 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
2002 .privileges(redfish::privileges::getLogEntry)
2003 .methods(boost::beast::http::verb::get)(
2004 [](const crow::Request&,
2005 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2006 const std::string& param) {
2007 const std::string& targetID = param;
2008
2009 uint64_t idInt = 0;
2010 auto [ptr, ec] = std::from_chars(
2011 targetID.data(), targetID.data() + targetID.size(), idInt);
2012 if (ec == std::errc::invalid_argument)
2013 {
2014 messages::resourceMissingAtURI(asyncResp->res, targetID);
2015 return;
2016 }
2017 if (ec == std::errc::result_out_of_range)
2018 {
2019 messages::resourceMissingAtURI(asyncResp->res, targetID);
2020 return;
2021 }
2022
2023 std::vector<std::filesystem::path> hostLoggerFiles;
2024 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2025 {
2026 BMCWEB_LOG_ERROR << "fail to get host log file path";
2027 return;
2028 }
2029
2030 size_t logCount = 0;
2031 uint64_t top = 1;
2032 std::vector<std::string> logEntries;
2033 // We can get specific entry by skip and top. For example, if we
2034 // want to get nth entry, we can set skip = n-1 and top = 1 to
2035 // get that entry
2036 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
2037 logEntries, logCount))
2038 {
2039 messages::internalError(asyncResp->res);
2040 return;
2041 }
2042
2043 if (!logEntries.empty())
2044 {
2045 fillHostLoggerEntryJson(targetID, logEntries[0],
2046 asyncResp->res.jsonValue);
2047 return;
2048 }
2049
2050 // Requested ID was not found
2051 messages::resourceMissingAtURI(asyncResp->res, targetID);
2052 });
2053}
2054
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002055inline void requestRoutesBMCLogServiceCollection(App& app)
2056{
2057 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002058 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002059 .methods(boost::beast::http::verb::get)(
2060 [](const crow::Request&,
2061 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2062 // Collections don't include the static data added by SubRoute
2063 // because it has a duplicate entry for members
2064 asyncResp->res.jsonValue["@odata.type"] =
2065 "#LogServiceCollection.LogServiceCollection";
2066 asyncResp->res.jsonValue["@odata.id"] =
2067 "/redfish/v1/Managers/bmc/LogServices";
2068 asyncResp->res.jsonValue["Name"] =
2069 "Open BMC Log Services Collection";
2070 asyncResp->res.jsonValue["Description"] =
2071 "Collection of LogServices for this Manager";
2072 nlohmann::json& logServiceArray =
2073 asyncResp->res.jsonValue["Members"];
2074 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002075#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002076 logServiceArray.push_back(
2077 {{"@odata.id",
2078 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002079#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002080#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002081 logServiceArray.push_back(
2082 {{"@odata.id",
2083 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002084#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002085 asyncResp->res.jsonValue["Members@odata.count"] =
2086 logServiceArray.size();
2087 });
2088}
Ed Tanous1da66f72018-07-27 16:13:37 -07002089
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002090inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002091{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002092 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002093 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002094 .methods(boost::beast::http::verb::get)(
2095 [](const crow::Request&,
2096 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002097
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002098 {
2099 asyncResp->res.jsonValue["@odata.type"] =
2100 "#LogService.v1_1_0.LogService";
2101 asyncResp->res.jsonValue["@odata.id"] =
2102 "/redfish/v1/Managers/bmc/LogServices/Journal";
2103 asyncResp->res.jsonValue["Name"] =
2104 "Open BMC Journal Log Service";
2105 asyncResp->res.jsonValue["Description"] =
2106 "BMC Journal Log Service";
2107 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2108 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302109
2110 std::pair<std::string, std::string> redfishDateTimeOffset =
2111 crow::utility::getDateTimeOffsetNow();
2112 asyncResp->res.jsonValue["DateTime"] =
2113 redfishDateTimeOffset.first;
2114 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2115 redfishDateTimeOffset.second;
2116
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002117 asyncResp->res.jsonValue["Entries"] = {
2118 {"@odata.id",
2119 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
2120 });
2121}
Jason M. Billse1f26342018-07-18 12:12:00 -07002122
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002123static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2124 sd_journal* journal,
2125 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002126{
2127 // Get the Log Entry contents
2128 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002129
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002130 std::string message;
2131 std::string_view syslogID;
2132 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2133 if (ret < 0)
2134 {
2135 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2136 << strerror(-ret);
2137 }
2138 if (!syslogID.empty())
2139 {
2140 message += std::string(syslogID) + ": ";
2141 }
2142
Ed Tanous39e77502019-03-04 17:35:53 -08002143 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002144 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002145 if (ret < 0)
2146 {
2147 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2148 return 1;
2149 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002150 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002151
2152 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002153 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002154 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002155 if (ret < 0)
2156 {
2157 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002158 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002159
2160 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002161 std::string entryTimeStr;
2162 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002163 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002164 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002165 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002166
2167 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002168 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002169 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002170 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2171 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002172 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002173 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002174 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002175 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002176 {"Severity", severity <= 2 ? "Critical"
2177 : severity <= 4 ? "Warning"
2178 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002179 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002180 {"Created", std::move(entryTimeStr)}};
2181 return 0;
2182}
2183
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002184inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002185{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002186 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002187 .privileges(redfish::privileges::getLogEntryCollection)
George Liu0fda0f12021-11-16 10:06:17 +08002188 .methods(
2189 boost::beast::http::verb::
2190 get)([](const crow::Request& req,
2191 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2192 static constexpr const long maxEntriesPerPage = 1000;
2193 uint64_t skip = 0;
2194 uint64_t top = maxEntriesPerPage; // Show max entries by default
2195 if (!getSkipParam(asyncResp, req, skip))
2196 {
2197 return;
2198 }
2199 if (!getTopParam(asyncResp, req, top))
2200 {
2201 return;
2202 }
2203 // Collections don't include the static data added by SubRoute
2204 // because it has a duplicate entry for members
2205 asyncResp->res.jsonValue["@odata.type"] =
2206 "#LogEntryCollection.LogEntryCollection";
2207 asyncResp->res.jsonValue["@odata.id"] =
2208 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2209 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2210 asyncResp->res.jsonValue["Description"] =
2211 "Collection of BMC Journal Entries";
2212 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2213 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002214
George Liu0fda0f12021-11-16 10:06:17 +08002215 // Go through the journal and use the timestamp to create a
2216 // unique ID for each entry
2217 sd_journal* journalTmp = nullptr;
2218 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2219 if (ret < 0)
2220 {
2221 BMCWEB_LOG_ERROR << "failed to open journal: "
2222 << strerror(-ret);
2223 messages::internalError(asyncResp->res);
2224 return;
2225 }
2226 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2227 journalTmp, sd_journal_close);
2228 journalTmp = nullptr;
2229 uint64_t entryCount = 0;
2230 // Reset the unique ID on the first entry
2231 bool firstEntry = true;
2232 SD_JOURNAL_FOREACH(journal.get())
2233 {
2234 entryCount++;
2235 // Handle paging using skip (number of entries to skip from
2236 // the start) and top (number of entries to display)
2237 if (entryCount <= skip || entryCount > skip + top)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002238 {
George Liu0fda0f12021-11-16 10:06:17 +08002239 continue;
2240 }
2241
2242 std::string idStr;
2243 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2244 {
2245 continue;
2246 }
2247
2248 if (firstEntry)
2249 {
2250 firstEntry = false;
2251 }
2252
2253 logEntryArray.push_back({});
2254 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2255 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2256 bmcJournalLogEntry) != 0)
2257 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002258 messages::internalError(asyncResp->res);
2259 return;
2260 }
George Liu0fda0f12021-11-16 10:06:17 +08002261 }
2262 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2263 if (skip + top < entryCount)
2264 {
2265 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2266 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2267 std::to_string(skip + top);
2268 }
2269 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002270}
Jason M. Billse1f26342018-07-18 12:12:00 -07002271
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002272inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002273{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002274 BMCWEB_ROUTE(app,
2275 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002276 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002277 .methods(boost::beast::http::verb::get)(
2278 [](const crow::Request&,
2279 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2280 const std::string& entryID) {
2281 // Convert the unique ID back to a timestamp to find the entry
2282 uint64_t ts = 0;
2283 uint64_t index = 0;
2284 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2285 {
2286 return;
2287 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002288
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002289 sd_journal* journalTmp = nullptr;
2290 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2291 if (ret < 0)
2292 {
2293 BMCWEB_LOG_ERROR << "failed to open journal: "
2294 << strerror(-ret);
2295 messages::internalError(asyncResp->res);
2296 return;
2297 }
2298 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2299 journal(journalTmp, sd_journal_close);
2300 journalTmp = nullptr;
2301 // Go to the timestamp in the log and move to the entry at the
2302 // index tracking the unique ID
2303 std::string idStr;
2304 bool firstEntry = true;
2305 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2306 if (ret < 0)
2307 {
2308 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2309 << strerror(-ret);
2310 messages::internalError(asyncResp->res);
2311 return;
2312 }
2313 for (uint64_t i = 0; i <= index; i++)
2314 {
2315 sd_journal_next(journal.get());
2316 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2317 {
2318 messages::internalError(asyncResp->res);
2319 return;
2320 }
2321 if (firstEntry)
2322 {
2323 firstEntry = false;
2324 }
2325 }
2326 // Confirm that the entry ID matches what was requested
2327 if (idStr != entryID)
2328 {
2329 messages::resourceMissingAtURI(asyncResp->res, entryID);
2330 return;
2331 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002332
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002333 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2334 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002335 {
2336 messages::internalError(asyncResp->res);
2337 return;
2338 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002339 });
2340}
2341
2342inline void requestRoutesBMCDumpService(App& app)
2343{
2344 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002345 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08002346 .methods(
2347 boost::beast::http::verb::
2348 get)([](const crow::Request&,
2349 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2350 asyncResp->res.jsonValue["@odata.id"] =
2351 "/redfish/v1/Managers/bmc/LogServices/Dump";
2352 asyncResp->res.jsonValue["@odata.type"] =
2353 "#LogService.v1_2_0.LogService";
2354 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2355 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2356 asyncResp->res.jsonValue["Id"] = "Dump";
2357 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302358
George Liu0fda0f12021-11-16 10:06:17 +08002359 std::pair<std::string, std::string> redfishDateTimeOffset =
2360 crow::utility::getDateTimeOffsetNow();
2361 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2362 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2363 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302364
George Liu0fda0f12021-11-16 10:06:17 +08002365 asyncResp->res.jsonValue["Entries"] = {
2366 {"@odata.id",
2367 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2368 asyncResp->res.jsonValue["Actions"] = {
2369 {"#LogService.ClearLog",
2370 {{"target",
2371 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog"}}},
2372 {"#LogService.CollectDiagnosticData",
2373 {{"target",
2374 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
2375 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002376}
2377
2378inline void requestRoutesBMCDumpEntryCollection(App& app)
2379{
2380
2381 /**
2382 * Functions triggers appropriate requests on DBus
2383 */
2384 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002385 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002386 .methods(boost::beast::http::verb::get)(
2387 [](const crow::Request&,
2388 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2389 asyncResp->res.jsonValue["@odata.type"] =
2390 "#LogEntryCollection.LogEntryCollection";
2391 asyncResp->res.jsonValue["@odata.id"] =
2392 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2393 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2394 asyncResp->res.jsonValue["Description"] =
2395 "Collection of BMC Dump Entries";
2396
2397 getDumpEntryCollection(asyncResp, "BMC");
2398 });
2399}
2400
2401inline void requestRoutesBMCDumpEntry(App& app)
2402{
2403 BMCWEB_ROUTE(app,
2404 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002405 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002406 .methods(boost::beast::http::verb::get)(
2407 [](const crow::Request&,
2408 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2409 const std::string& param) {
2410 getDumpEntryById(asyncResp, param, "BMC");
2411 });
2412 BMCWEB_ROUTE(app,
2413 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002414 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002415 .methods(boost::beast::http::verb::delete_)(
2416 [](const crow::Request&,
2417 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2418 const std::string& param) {
2419 deleteDumpEntry(asyncResp, param, "bmc");
2420 });
2421}
2422
2423inline void requestRoutesBMCDumpCreate(App& app)
2424{
2425
George Liu0fda0f12021-11-16 10:06:17 +08002426 BMCWEB_ROUTE(
2427 app,
2428 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002429 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002430 .methods(boost::beast::http::verb::post)(
2431 [](const crow::Request& req,
2432 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2433 createDump(asyncResp, req, "BMC");
2434 });
2435}
2436
2437inline void requestRoutesBMCDumpClear(App& app)
2438{
George Liu0fda0f12021-11-16 10:06:17 +08002439 BMCWEB_ROUTE(
2440 app,
2441 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002442 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002443 .methods(boost::beast::http::verb::post)(
2444 [](const crow::Request&,
2445 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2446 clearDump(asyncResp, "BMC");
2447 });
2448}
2449
2450inline void requestRoutesSystemDumpService(App& app)
2451{
2452 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002453 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002454 .methods(boost::beast::http::verb::get)(
2455 [](const crow::Request&,
2456 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2457
2458 {
2459 asyncResp->res.jsonValue["@odata.id"] =
2460 "/redfish/v1/Systems/system/LogServices/Dump";
2461 asyncResp->res.jsonValue["@odata.type"] =
2462 "#LogService.v1_2_0.LogService";
2463 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2464 asyncResp->res.jsonValue["Description"] =
2465 "System Dump LogService";
2466 asyncResp->res.jsonValue["Id"] = "Dump";
2467 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302468
2469 std::pair<std::string, std::string> redfishDateTimeOffset =
2470 crow::utility::getDateTimeOffsetNow();
2471 asyncResp->res.jsonValue["DateTime"] =
2472 redfishDateTimeOffset.first;
2473 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2474 redfishDateTimeOffset.second;
2475
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002476 asyncResp->res.jsonValue["Entries"] = {
2477 {"@odata.id",
2478 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2479 asyncResp->res.jsonValue["Actions"] = {
2480 {"#LogService.ClearLog",
2481 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002482 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002483 {"#LogService.CollectDiagnosticData",
2484 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002485 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002486 });
2487}
2488
2489inline void requestRoutesSystemDumpEntryCollection(App& app)
2490{
2491
2492 /**
2493 * Functions triggers appropriate requests on DBus
2494 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002495 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002496 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002497 .methods(boost::beast::http::verb::get)(
2498 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002499 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002500 asyncResp->res.jsonValue["@odata.type"] =
2501 "#LogEntryCollection.LogEntryCollection";
2502 asyncResp->res.jsonValue["@odata.id"] =
2503 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2504 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2505 asyncResp->res.jsonValue["Description"] =
2506 "Collection of System Dump Entries";
2507
2508 getDumpEntryCollection(asyncResp, "System");
2509 });
2510}
2511
2512inline void requestRoutesSystemDumpEntry(App& app)
2513{
2514 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002515 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002516 .privileges(redfish::privileges::getLogEntry)
2517
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002518 .methods(boost::beast::http::verb::get)(
2519 [](const crow::Request&,
2520 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2521 const std::string& param) {
2522 getDumpEntryById(asyncResp, param, "System");
2523 });
2524
2525 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002526 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002527 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002528 .methods(boost::beast::http::verb::delete_)(
2529 [](const crow::Request&,
2530 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2531 const std::string& param) {
2532 deleteDumpEntry(asyncResp, param, "system");
2533 });
2534}
2535
2536inline void requestRoutesSystemDumpCreate(App& app)
2537{
George Liu0fda0f12021-11-16 10:06:17 +08002538 BMCWEB_ROUTE(
2539 app,
2540 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002541 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002542 .methods(boost::beast::http::verb::post)(
2543 [](const crow::Request& req,
2544 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2545
2546 { createDump(asyncResp, req, "System"); });
2547}
2548
2549inline void requestRoutesSystemDumpClear(App& app)
2550{
George Liu0fda0f12021-11-16 10:06:17 +08002551 BMCWEB_ROUTE(
2552 app,
2553 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002554 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002555 .methods(boost::beast::http::verb::post)(
2556 [](const crow::Request&,
2557 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2558
2559 { clearDump(asyncResp, "System"); });
2560}
2561
2562inline void requestRoutesCrashdumpService(App& app)
2563{
2564 // Note: Deviated from redfish privilege registry for GET & HEAD
2565 // method for security reasons.
2566 /**
2567 * Functions triggers appropriate requests on DBus
2568 */
2569 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002570 // This is incorrect, should be:
2571 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002572 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002573 .methods(
2574 boost::beast::http::verb::
2575 get)([](const crow::Request&,
2576 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2577 // Copy over the static data to include the entries added by
2578 // SubRoute
2579 asyncResp->res.jsonValue["@odata.id"] =
2580 "/redfish/v1/Systems/system/LogServices/Crashdump";
2581 asyncResp->res.jsonValue["@odata.type"] =
2582 "#LogService.v1_2_0.LogService";
2583 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2584 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2585 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2586 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2587 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302588
2589 std::pair<std::string, std::string> redfishDateTimeOffset =
2590 crow::utility::getDateTimeOffsetNow();
2591 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2592 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2593 redfishDateTimeOffset.second;
2594
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002595 asyncResp->res.jsonValue["Entries"] = {
2596 {"@odata.id",
2597 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2598 asyncResp->res.jsonValue["Actions"] = {
2599 {"#LogService.ClearLog",
George Liu0fda0f12021-11-16 10:06:17 +08002600 {{"target",
2601 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002602 {"#LogService.CollectDiagnosticData",
George Liu0fda0f12021-11-16 10:06:17 +08002603 {{"target",
2604 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002605 });
2606}
2607
2608void inline requestRoutesCrashdumpClear(App& app)
2609{
George Liu0fda0f12021-11-16 10:06:17 +08002610 BMCWEB_ROUTE(
2611 app,
2612 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002613 // This is incorrect, should be:
2614 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002615 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002616 .methods(boost::beast::http::verb::post)(
2617 [](const crow::Request&,
2618 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2619 crow::connections::systemBus->async_method_call(
2620 [asyncResp](const boost::system::error_code ec,
2621 const std::string&) {
2622 if (ec)
2623 {
2624 messages::internalError(asyncResp->res);
2625 return;
2626 }
2627 messages::success(asyncResp->res);
2628 },
2629 crashdumpObject, crashdumpPath, deleteAllInterface,
2630 "DeleteAll");
2631 });
2632}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002633
zhanghch058d1b46d2021-04-01 11:18:24 +08002634static void
2635 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2636 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002637{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002638 auto getStoredLogCallback =
2639 [asyncResp, logID, &logEntryJson](
2640 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002641 const std::vector<
2642 std::pair<std::string, dbus::utility::DbusVariantType>>&
2643 params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002644 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002645 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002646 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2647 if (ec.value() ==
2648 boost::system::linux_error::bad_request_descriptor)
2649 {
2650 messages::resourceNotFound(asyncResp->res, "LogEntry",
2651 logID);
2652 }
2653 else
2654 {
2655 messages::internalError(asyncResp->res);
2656 }
2657 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002658 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002659
Johnathan Mantey043a0532020-03-10 17:15:28 -07002660 std::string timestamp{};
2661 std::string filename{};
2662 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002663 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002664
2665 if (filename.empty() || timestamp.empty())
2666 {
2667 messages::resourceMissingAtURI(asyncResp->res, logID);
2668 return;
2669 }
2670
2671 std::string crashdumpURI =
2672 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2673 logID + "/" + filename;
Ed Tanousd0dbeef2021-07-01 08:46:46 -07002674 logEntryJson = {{"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002675 {"@odata.id", "/redfish/v1/Systems/system/"
2676 "LogServices/Crashdump/Entries/" +
2677 logID},
2678 {"Name", "CPU Crashdump"},
2679 {"Id", logID},
2680 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002681 {"AdditionalDataURI", std::move(crashdumpURI)},
2682 {"DiagnosticDataType", "OEM"},
2683 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002684 {"Created", std::move(timestamp)}};
2685 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002686 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002687 std::move(getStoredLogCallback), crashdumpObject,
2688 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002689 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002690}
2691
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002692inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002693{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002694 // Note: Deviated from redfish privilege registry for GET & HEAD
2695 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002696 /**
2697 * Functions triggers appropriate requests on DBus
2698 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002699 BMCWEB_ROUTE(app,
2700 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002701 // This is incorrect, should be.
2702 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002703 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002704 .methods(
2705 boost::beast::http::verb::
2706 get)([](const crow::Request&,
2707 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2708 // Collections don't include the static data added by SubRoute
2709 // because it has a duplicate entry for members
2710 auto getLogEntriesCallback = [asyncResp](
2711 const boost::system::error_code ec,
2712 const std::vector<std::string>&
2713 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002714 if (ec)
2715 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002716 if (ec.value() !=
2717 boost::system::errc::no_such_file_or_directory)
2718 {
2719 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2720 << ec.message();
2721 messages::internalError(asyncResp->res);
2722 return;
2723 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002724 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002725 asyncResp->res.jsonValue["@odata.type"] =
2726 "#LogEntryCollection.LogEntryCollection";
2727 asyncResp->res.jsonValue["@odata.id"] =
2728 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2729 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2730 asyncResp->res.jsonValue["Description"] =
2731 "Collection of Crashdump Entries";
2732 nlohmann::json& logEntryArray =
2733 asyncResp->res.jsonValue["Members"];
2734 logEntryArray = nlohmann::json::array();
2735 std::vector<std::string> logIDs;
2736 // Get the list of log entries and build up an empty array big
2737 // enough to hold them
2738 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002739 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002740 // Get the log ID
2741 std::size_t lastPos = objpath.rfind('/');
2742 if (lastPos == std::string::npos)
2743 {
2744 continue;
2745 }
2746 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002747
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002748 // Add a space for the log entry to the array
2749 logEntryArray.push_back({});
2750 }
2751 // Now go through and set up async calls to fill in the entries
2752 size_t index = 0;
2753 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002754 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002755 // Add the log entry to the array
2756 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002757 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002758 asyncResp->res.jsonValue["Members@odata.count"] =
2759 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002760 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002761 crow::connections::systemBus->async_method_call(
2762 std::move(getLogEntriesCallback),
2763 "xyz.openbmc_project.ObjectMapper",
2764 "/xyz/openbmc_project/object_mapper",
2765 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2766 std::array<const char*, 1>{crashdumpInterface});
2767 });
2768}
Ed Tanous1da66f72018-07-27 16:13:37 -07002769
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002770inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002771{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002772 // Note: Deviated from redfish privilege registry for GET & HEAD
2773 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002774
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002775 BMCWEB_ROUTE(
2776 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002777 // this is incorrect, should be
2778 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002779 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002780 .methods(boost::beast::http::verb::get)(
2781 [](const crow::Request&,
2782 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2783 const std::string& param) {
2784 const std::string& logID = param;
2785 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2786 });
2787}
Ed Tanous1da66f72018-07-27 16:13:37 -07002788
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002789inline void requestRoutesCrashdumpFile(App& app)
2790{
2791 // Note: Deviated from redfish privilege registry for GET & HEAD
2792 // method for security reasons.
2793 BMCWEB_ROUTE(
2794 app,
2795 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002796 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002797 .methods(boost::beast::http::verb::get)(
2798 [](const crow::Request&,
2799 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2800 const std::string& logID, const std::string& fileName) {
2801 auto getStoredLogCallback =
2802 [asyncResp, logID, fileName](
2803 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002804 const std::vector<std::pair<
2805 std::string, dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002806 resp) {
2807 if (ec)
2808 {
2809 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2810 << ec.message();
2811 messages::internalError(asyncResp->res);
2812 return;
2813 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002814
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002815 std::string dbusFilename{};
2816 std::string dbusTimestamp{};
2817 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002818
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002819 parseCrashdumpParameters(resp, dbusFilename,
2820 dbusTimestamp, dbusFilepath);
2821
2822 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2823 dbusFilepath.empty())
2824 {
2825 messages::resourceMissingAtURI(asyncResp->res,
2826 fileName);
2827 return;
2828 }
2829
2830 // Verify the file name parameter is correct
2831 if (fileName != dbusFilename)
2832 {
2833 messages::resourceMissingAtURI(asyncResp->res,
2834 fileName);
2835 return;
2836 }
2837
2838 if (!std::filesystem::exists(dbusFilepath))
2839 {
2840 messages::resourceMissingAtURI(asyncResp->res,
2841 fileName);
2842 return;
2843 }
2844 std::ifstream ifs(dbusFilepath, std::ios::in |
2845 std::ios::binary |
2846 std::ios::ate);
2847 std::ifstream::pos_type fileSize = ifs.tellg();
2848 if (fileSize < 0)
2849 {
2850 messages::generalError(asyncResp->res);
2851 return;
2852 }
2853 ifs.seekg(0, std::ios::beg);
2854
2855 auto crashData = std::make_unique<char[]>(
2856 static_cast<unsigned int>(fileSize));
2857
2858 ifs.read(crashData.get(), static_cast<int>(fileSize));
2859
2860 // The cast to std::string is intentional in order to
2861 // use the assign() that applies move mechanics
2862 asyncResp->res.body().assign(
2863 static_cast<std::string>(crashData.get()));
2864
2865 // Configure this to be a file download when accessed
2866 // from a browser
2867 asyncResp->res.addHeader("Content-Disposition",
2868 "attachment");
2869 };
2870 crow::connections::systemBus->async_method_call(
2871 std::move(getStoredLogCallback), crashdumpObject,
2872 crashdumpPath + std::string("/") + logID,
2873 "org.freedesktop.DBus.Properties", "GetAll",
2874 crashdumpInterface);
2875 });
2876}
2877
2878inline void requestRoutesCrashdumpCollect(App& app)
2879{
2880 // Note: Deviated from redfish privilege registry for GET & HEAD
2881 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002882 BMCWEB_ROUTE(
2883 app,
2884 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002885 // The below is incorrect; Should be ConfigureManager
2886 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002887 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002888 .methods(
2889 boost::beast::http::verb::
2890 post)([](const crow::Request& req,
2891 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2892 std::string diagnosticDataType;
2893 std::string oemDiagnosticDataType;
2894 if (!redfish::json_util::readJson(
2895 req, asyncResp->res, "DiagnosticDataType",
2896 diagnosticDataType, "OEMDiagnosticDataType",
2897 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002898 {
James Feist46229572020-02-19 15:11:58 -08002899 return;
2900 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002901
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002902 if (diagnosticDataType != "OEM")
2903 {
2904 BMCWEB_LOG_ERROR
2905 << "Only OEM DiagnosticDataType supported for Crashdump";
2906 messages::actionParameterValueFormatError(
2907 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2908 "CollectDiagnosticData");
2909 return;
2910 }
2911
Ed Tanous98be3e32021-09-16 15:05:36 -07002912 auto collectCrashdumpCallback = [asyncResp,
2913 payload(task::Payload(req))](
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002914 const boost::system::error_code
2915 ec,
Ed Tanous98be3e32021-09-16 15:05:36 -07002916 const std::string&) mutable {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002917 if (ec)
2918 {
2919 if (ec.value() ==
2920 boost::system::errc::operation_not_supported)
2921 {
2922 messages::resourceInStandby(asyncResp->res);
2923 }
2924 else if (ec.value() ==
2925 boost::system::errc::device_or_resource_busy)
2926 {
2927 messages::serviceTemporarilyUnavailable(asyncResp->res,
2928 "60");
2929 }
2930 else
2931 {
2932 messages::internalError(asyncResp->res);
2933 }
2934 return;
2935 }
George Liu0fda0f12021-11-16 10:06:17 +08002936 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2937 [](boost::system::error_code err,
2938 sdbusplus::message::message&,
2939 const std::shared_ptr<task::TaskData>& taskData) {
2940 if (!err)
2941 {
2942 taskData->messages.emplace_back(
2943 messages::taskCompletedOK(
2944 std::to_string(taskData->index)));
2945 taskData->state = "Completed";
2946 }
2947 return task::completed;
2948 },
2949 "type='signal',interface='org.freedesktop.DBus."
2950 "Properties',"
2951 "member='PropertiesChanged',arg0namespace='com.intel.crashdump'");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002952 task->startTimer(std::chrono::minutes(5));
2953 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -07002954 task->payload.emplace(std::move(payload));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002955 };
2956
2957 if (oemDiagnosticDataType == "OnDemand")
2958 {
2959 crow::connections::systemBus->async_method_call(
2960 std::move(collectCrashdumpCallback), crashdumpObject,
2961 crashdumpPath, crashdumpOnDemandInterface,
2962 "GenerateOnDemandLog");
2963 }
2964 else if (oemDiagnosticDataType == "Telemetry")
2965 {
2966 crow::connections::systemBus->async_method_call(
2967 std::move(collectCrashdumpCallback), crashdumpObject,
2968 crashdumpPath, crashdumpTelemetryInterface,
2969 "GenerateTelemetryLog");
2970 }
2971 else
2972 {
2973 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2974 << oemDiagnosticDataType;
2975 messages::actionParameterValueFormatError(
2976 asyncResp->res, oemDiagnosticDataType,
2977 "OEMDiagnosticDataType", "CollectDiagnosticData");
2978 return;
2979 }
2980 });
2981}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002982
Andrew Geisslercb92c032018-08-17 07:56:14 -07002983/**
2984 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2985 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002986inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002987{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002988 /**
2989 * Function handles POST method request.
2990 * The Clear Log actions does not require any parameter.The action deletes
2991 * all entries found in the Entries collection for this Log Service.
2992 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002993
George Liu0fda0f12021-11-16 10:06:17 +08002994 BMCWEB_ROUTE(
2995 app,
2996 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002997 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002998 .methods(boost::beast::http::verb::post)(
2999 [](const crow::Request&,
3000 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3001 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003002
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003003 // Process response from Logging service.
3004 auto respHandler = [asyncResp](
3005 const boost::system::error_code ec) {
3006 BMCWEB_LOG_DEBUG
3007 << "doClearLog resp_handler callback: Done";
3008 if (ec)
3009 {
3010 // TODO Handle for specific error code
3011 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
3012 << ec;
3013 asyncResp->res.result(
3014 boost::beast::http::status::internal_server_error);
3015 return;
3016 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003017
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003018 asyncResp->res.result(
3019 boost::beast::http::status::no_content);
3020 };
3021
3022 // Make call to Logging service to request Clear Log
3023 crow::connections::systemBus->async_method_call(
3024 respHandler, "xyz.openbmc_project.Logging",
3025 "/xyz/openbmc_project/logging",
3026 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3027 });
3028}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003029
3030/****************************************************
3031 * Redfish PostCode interfaces
3032 * using DBUS interface: getPostCodesTS
3033 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003034inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003035{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003036 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003037 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08003038 .methods(
3039 boost::beast::http::verb::
3040 get)([](const crow::Request&,
3041 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3042 asyncResp->res.jsonValue = {
3043 {"@odata.id",
3044 "/redfish/v1/Systems/system/LogServices/PostCodes"},
3045 {"@odata.type", "#LogService.v1_1_0.LogService"},
3046 {"Name", "POST Code Log Service"},
3047 {"Description", "POST Code Log Service"},
3048 {"Id", "BIOS POST Code Log"},
3049 {"OverWritePolicy", "WrapsWhenFull"},
3050 {"Entries",
3051 {{"@odata.id",
3052 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05303053
George Liu0fda0f12021-11-16 10:06:17 +08003054 std::pair<std::string, std::string> redfishDateTimeOffset =
3055 crow::utility::getDateTimeOffsetNow();
3056 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3057 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3058 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303059
George Liu0fda0f12021-11-16 10:06:17 +08003060 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3061 {"target",
3062 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3063 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003064}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003065
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003066inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003067{
George Liu0fda0f12021-11-16 10:06:17 +08003068 BMCWEB_ROUTE(
3069 app,
3070 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003071 // The following privilege is incorrect; It should be ConfigureManager
3072 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003073 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003074 .methods(boost::beast::http::verb::post)(
3075 [](const crow::Request&,
3076 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3077 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003078
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003079 // Make call to post-code service to request clear all
3080 crow::connections::systemBus->async_method_call(
3081 [asyncResp](const boost::system::error_code ec) {
3082 if (ec)
3083 {
3084 // TODO Handle for specific error code
3085 BMCWEB_LOG_ERROR
3086 << "doClearPostCodes resp_handler got error "
3087 << ec;
3088 asyncResp->res.result(boost::beast::http::status::
3089 internal_server_error);
3090 messages::internalError(asyncResp->res);
3091 return;
3092 }
3093 },
3094 "xyz.openbmc_project.State.Boot.PostCode0",
3095 "/xyz/openbmc_project/State/Boot/PostCode0",
3096 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3097 });
3098}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003099
3100static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003101 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303102 const boost::container::flat_map<
3103 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003104 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3105 const uint64_t skip = 0, const uint64_t top = 0)
3106{
3107 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003108 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05303109 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003110
3111 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003112 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003113
3114 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303115 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3116 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003117 {
3118 currentCodeIndex++;
3119 std::string postcodeEntryID =
3120 "B" + std::to_string(bootIndex) + "-" +
3121 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3122
3123 uint64_t usecSinceEpoch = code.first;
3124 uint64_t usTimeOffset = 0;
3125
3126 if (1 == currentCodeIndex)
3127 { // already incremented
3128 firstCodeTimeUs = code.first;
3129 }
3130 else
3131 {
3132 usTimeOffset = code.first - firstCodeTimeUs;
3133 }
3134
3135 // skip if no specific codeIndex is specified and currentCodeIndex does
3136 // not fall between top and skip
3137 if ((codeIndex == 0) &&
3138 (currentCodeIndex <= skip || currentCodeIndex > top))
3139 {
3140 continue;
3141 }
3142
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003143 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003144 // currentIndex
3145 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3146 {
3147 // This is done for simplicity. 1st entry is needed to calculate
3148 // time offset. To improve efficiency, one can get to the entry
3149 // directly (possibly with flatmap's nth method)
3150 continue;
3151 }
3152
3153 // currentCodeIndex is within top and skip or equal to specified code
3154 // index
3155
3156 // Get the Created time from the timestamp
3157 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003158 entryTimeStr =
3159 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003160
3161 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3162 std::ostringstream hexCode;
3163 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303164 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003165 std::ostringstream timeOffsetStr;
3166 // Set Fixed -Point Notation
3167 timeOffsetStr << std::fixed;
3168 // Set precision to 4 digits
3169 timeOffsetStr << std::setprecision(4);
3170 // Add double to stream
3171 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3172 std::vector<std::string> messageArgs = {
3173 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3174
3175 // Get MessageArgs template from message registry
3176 std::string msg;
3177 if (message != nullptr)
3178 {
3179 msg = message->message;
3180
3181 // fill in this post code value
3182 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003183 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003184 {
3185 std::string argStr = "%" + std::to_string(++i);
3186 size_t argPos = msg.find(argStr);
3187 if (argPos != std::string::npos)
3188 {
3189 msg.replace(argPos, argStr.length(), messageArg);
3190 }
3191 }
3192 }
3193
Tim Leed4342a92020-04-27 11:47:58 +08003194 // Get Severity template from message registry
3195 std::string severity;
3196 if (message != nullptr)
3197 {
3198 severity = message->severity;
3199 }
3200
ZhikuiRena3316fc2020-01-29 14:58:08 -08003201 // add to AsyncResp
3202 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003203 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003204 bmcLogEntry = {
3205 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3206 {"@odata.id",
3207 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3208 postcodeEntryID},
3209 {"Name", "POST Code Log Entry"},
3210 {"Id", postcodeEntryID},
3211 {"Message", std::move(msg)},
3212 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3213 {"MessageArgs", std::move(messageArgs)},
3214 {"EntryType", "Event"},
3215 {"Severity", std::move(severity)},
3216 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003217 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3218 {
3219 bmcLogEntry["AdditionalDataURI"] =
3220 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3221 postcodeEntryID + "/attachment";
3222 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003223 }
3224}
3225
zhanghch058d1b46d2021-04-01 11:18:24 +08003226static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003227 const uint16_t bootIndex,
3228 const uint64_t codeIndex)
3229{
3230 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303231 [aResp, bootIndex,
3232 codeIndex](const boost::system::error_code ec,
3233 const boost::container::flat_map<
3234 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3235 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003236 if (ec)
3237 {
3238 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3239 messages::internalError(aResp->res);
3240 return;
3241 }
3242
3243 // skip the empty postcode boots
3244 if (postcode.empty())
3245 {
3246 return;
3247 }
3248
3249 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3250
3251 aResp->res.jsonValue["Members@odata.count"] =
3252 aResp->res.jsonValue["Members"].size();
3253 },
Jonathan Doman15124762021-01-07 17:54:17 -08003254 "xyz.openbmc_project.State.Boot.PostCode0",
3255 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003256 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3257 bootIndex);
3258}
3259
zhanghch058d1b46d2021-04-01 11:18:24 +08003260static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003261 const uint16_t bootIndex,
3262 const uint16_t bootCount,
3263 const uint64_t entryCount, const uint64_t skip,
3264 const uint64_t top)
3265{
3266 crow::connections::systemBus->async_method_call(
3267 [aResp, bootIndex, bootCount, entryCount, skip,
3268 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303269 const boost::container::flat_map<
3270 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3271 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003272 if (ec)
3273 {
3274 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3275 messages::internalError(aResp->res);
3276 return;
3277 }
3278
3279 uint64_t endCount = entryCount;
3280 if (!postcode.empty())
3281 {
3282 endCount = entryCount + postcode.size();
3283
3284 if ((skip < endCount) && ((top + skip) > entryCount))
3285 {
3286 uint64_t thisBootSkip =
3287 std::max(skip, entryCount) - entryCount;
3288 uint64_t thisBootTop =
3289 std::min(top + skip, endCount) - entryCount;
3290
3291 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3292 thisBootSkip, thisBootTop);
3293 }
3294 aResp->res.jsonValue["Members@odata.count"] = endCount;
3295 }
3296
3297 // continue to previous bootIndex
3298 if (bootIndex < bootCount)
3299 {
3300 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3301 bootCount, endCount, skip, top);
3302 }
3303 else
3304 {
3305 aResp->res.jsonValue["Members@odata.nextLink"] =
George Liu0fda0f12021-11-16 10:06:17 +08003306 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
ZhikuiRena3316fc2020-01-29 14:58:08 -08003307 std::to_string(skip + top);
3308 }
3309 },
Jonathan Doman15124762021-01-07 17:54:17 -08003310 "xyz.openbmc_project.State.Boot.PostCode0",
3311 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003312 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3313 bootIndex);
3314}
3315
zhanghch058d1b46d2021-04-01 11:18:24 +08003316static void
3317 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3318 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003319{
3320 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003321 sdbusplus::asio::getProperty<uint16_t>(
3322 *crow::connections::systemBus,
3323 "xyz.openbmc_project.State.Boot.PostCode0",
3324 "/xyz/openbmc_project/State/Boot/PostCode0",
3325 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3326 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3327 const uint16_t bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003328 if (ec)
3329 {
3330 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3331 messages::internalError(aResp->res);
3332 return;
3333 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003334 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3335 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003336}
3337
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003338inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003339{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003340 BMCWEB_ROUTE(app,
3341 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003342 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003343 .methods(boost::beast::http::verb::get)(
3344 [](const crow::Request& req,
3345 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3346 asyncResp->res.jsonValue["@odata.type"] =
3347 "#LogEntryCollection.LogEntryCollection";
3348 asyncResp->res.jsonValue["@odata.id"] =
3349 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3350 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3351 asyncResp->res.jsonValue["Description"] =
3352 "Collection of POST Code Log Entries";
3353 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3354 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003355
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003356 uint64_t skip = 0;
3357 uint64_t top = maxEntriesPerPage; // Show max entries by default
3358 if (!getSkipParam(asyncResp, req, skip))
3359 {
3360 return;
3361 }
3362 if (!getTopParam(asyncResp, req, top))
3363 {
3364 return;
3365 }
3366 getCurrentBootNumber(asyncResp, skip, top);
3367 });
3368}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003369
George Liu647b3cd2021-07-05 12:43:56 +08003370/**
3371 * @brief Parse post code ID and get the current value and index value
3372 * eg: postCodeID=B1-2, currentValue=1, index=2
3373 *
3374 * @param[in] postCodeID Post Code ID
3375 * @param[out] currentValue Current value
3376 * @param[out] index Index value
3377 *
3378 * @return bool true if the parsing is successful, false the parsing fails
3379 */
3380inline static bool parsePostCode(const std::string& postCodeID,
3381 uint64_t& currentValue, uint16_t& index)
3382{
3383 std::vector<std::string> split;
3384 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3385 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3386 {
3387 return false;
3388 }
3389
3390 const char* start = split[0].data() + 1;
3391 const char* end = split[0].data() + split[0].size();
3392 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3393
3394 if (ptrIndex != end || ecIndex != std::errc())
3395 {
3396 return false;
3397 }
3398
3399 start = split[1].data();
3400 end = split[1].data() + split[1].size();
3401 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3402 if (ptrValue != end || ecValue != std::errc())
3403 {
3404 return false;
3405 }
3406
3407 return true;
3408}
3409
3410inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3411{
George Liu0fda0f12021-11-16 10:06:17 +08003412 BMCWEB_ROUTE(
3413 app,
3414 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003415 .privileges(redfish::privileges::getLogEntry)
3416 .methods(boost::beast::http::verb::get)(
3417 [](const crow::Request& req,
3418 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3419 const std::string& postCodeID) {
3420 if (!http_helpers::isOctetAccepted(
3421 req.getHeaderValue("Accept")))
3422 {
3423 asyncResp->res.result(
3424 boost::beast::http::status::bad_request);
3425 return;
3426 }
3427
3428 uint64_t currentValue = 0;
3429 uint16_t index = 0;
3430 if (!parsePostCode(postCodeID, currentValue, index))
3431 {
3432 messages::resourceNotFound(asyncResp->res, "LogEntry",
3433 postCodeID);
3434 return;
3435 }
3436
3437 crow::connections::systemBus->async_method_call(
3438 [asyncResp, postCodeID, currentValue](
3439 const boost::system::error_code ec,
3440 const std::vector<std::tuple<
3441 uint64_t, std::vector<uint8_t>>>& postcodes) {
3442 if (ec.value() == EBADR)
3443 {
3444 messages::resourceNotFound(asyncResp->res,
3445 "LogEntry", postCodeID);
3446 return;
3447 }
3448 if (ec)
3449 {
3450 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3451 messages::internalError(asyncResp->res);
3452 return;
3453 }
3454
3455 size_t value = static_cast<size_t>(currentValue) - 1;
3456 if (value == std::string::npos ||
3457 postcodes.size() < currentValue)
3458 {
3459 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3460 messages::resourceNotFound(asyncResp->res,
3461 "LogEntry", postCodeID);
3462 return;
3463 }
3464
3465 auto& [tID, code] = postcodes[value];
3466 if (code.empty())
3467 {
3468 BMCWEB_LOG_INFO << "No found post code data";
3469 messages::resourceNotFound(asyncResp->res,
3470 "LogEntry", postCodeID);
3471 return;
3472 }
3473
3474 std::string_view strData(
3475 reinterpret_cast<const char*>(code.data()),
3476 code.size());
3477
3478 asyncResp->res.addHeader("Content-Type",
3479 "application/octet-stream");
3480 asyncResp->res.addHeader("Content-Transfer-Encoding",
3481 "Base64");
3482 asyncResp->res.body() =
3483 crow::utility::base64encode(strData);
3484 },
3485 "xyz.openbmc_project.State.Boot.PostCode0",
3486 "/xyz/openbmc_project/State/Boot/PostCode0",
3487 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3488 index);
3489 });
3490}
3491
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003492inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003493{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003494 BMCWEB_ROUTE(
3495 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003496 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003497 .methods(boost::beast::http::verb::get)(
3498 [](const crow::Request&,
3499 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3500 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003501 uint16_t bootIndex = 0;
3502 uint64_t codeIndex = 0;
3503 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003504 {
3505 // Requested ID was not found
3506 messages::resourceMissingAtURI(asyncResp->res, targetID);
3507 return;
3508 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003509 if (bootIndex == 0 || codeIndex == 0)
3510 {
3511 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3512 << targetID;
3513 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003514
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003515 asyncResp->res.jsonValue["@odata.type"] =
3516 "#LogEntry.v1_4_0.LogEntry";
3517 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08003518 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003519 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3520 asyncResp->res.jsonValue["Description"] =
3521 "Collection of POST Code Log Entries";
3522 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3523 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003524
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003525 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3526 });
3527}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003528
Ed Tanous1da66f72018-07-27 16:13:37 -07003529} // namespace redfish