blob: 2c9ae3a9ce428af277e7bb01766be573c45128fc [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
George Liu647b3cd2021-07-05 12:43:56 +080018#include "http_utility.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060025#include <unistd.h>
Jason M. Billse1f26342018-07-18 12:12:00 -070026
John Edward Broadbent7e860f12021-04-08 15:57:16 -070027#include <app.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060028#include <boost/algorithm/string/replace.hpp>
Jason M. Bills4851d452019-03-28 11:27:48 -070029#include <boost/algorithm/string/split.hpp>
30#include <boost/beast/core/span.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060031#include <boost/beast/http.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070032#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080033#include <boost/system/linux_error.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070034#include <error_messages.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070035#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050036
George Liu647b3cd2021-07-05 12:43:56 +080037#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070038#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080039#include <optional>
Jason M. Billscd225da2019-05-08 15:31:57 -070040#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080041#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070042
43namespace redfish
44{
45
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046constexpr char const* crashdumpObject = "com.intel.crashdump";
47constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050048constexpr char const* crashdumpInterface = "com.intel.crashdump";
49constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070050 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050051constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070052 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070053constexpr char const* crashdumpTelemetryInterface =
54 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070055
Jason M. Bills4851d452019-03-28 11:27:48 -070056namespace message_registries
57{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050058static const Message* getMessageFromRegistry(
59 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070060 const boost::beast::span<const MessageEntry> registry)
61{
62 boost::beast::span<const MessageEntry>::const_iterator messageIt =
63 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050064 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070065 return !std::strcmp(messageEntry.first,
66 messageKey.c_str());
67 });
68 if (messageIt != registry.cend())
69 {
70 return &messageIt->second;
71 }
72
73 return nullptr;
74}
75
Gunnar Mills1214b7e2020-06-04 10:11:30 -050076static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070077{
78 // Redfish MessageIds are in the form
79 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
80 // the right Message
81 std::vector<std::string> fields;
82 fields.reserve(4);
83 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050084 std::string& registryName = fields[0];
85 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070086
87 // Find the right registry and check it for the MessageKey
88 if (std::string(base::header.registryPrefix) == registryName)
89 {
90 return getMessageFromRegistry(
91 messageKey, boost::beast::span<const MessageEntry>(base::registry));
92 }
93 if (std::string(openbmc::header.registryPrefix) == registryName)
94 {
95 return getMessageFromRegistry(
96 messageKey,
97 boost::beast::span<const MessageEntry>(openbmc::registry));
98 }
99 return nullptr;
100}
101} // namespace message_registries
102
James Feistf6150402019-01-08 10:36:20 -0800103namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700104
Andrew Geisslercb92c032018-08-17 07:56:14 -0700105using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500106 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
107 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700108
109using GetManagedObjectsType = boost::container::flat_map<
110 sdbusplus::message::object_path,
111 boost::container::flat_map<std::string, GetManagedPropertyType>>;
112
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500113inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700114{
Ed Tanousd4d25792020-09-29 15:15:03 -0700115 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
116 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
117 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
118 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700119 {
120 return "Critical";
121 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700122 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
123 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
124 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700125 {
126 return "OK";
127 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700128 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700129 {
130 return "Warning";
131 }
132 return "";
133}
134
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700135inline static int getJournalMetadata(sd_journal* journal,
136 const std::string_view& field,
137 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700138{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500139 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700140 size_t length = 0;
141 int ret = 0;
142 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700143 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500144 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700145 if (ret < 0)
146 {
147 return ret;
148 }
Ed Tanous39e77502019-03-04 17:35:53 -0800149 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700150 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000151 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700152 return ret;
153}
154
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700155inline static int getJournalMetadata(sd_journal* journal,
156 const std::string_view& field,
157 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700158{
159 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800160 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700161 // Get the metadata from the requested field of the journal entry
162 ret = getJournalMetadata(journal, field, metadata);
163 if (ret < 0)
164 {
165 return ret;
166 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000167 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700168 return ret;
169}
170
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700171inline static bool getEntryTimestamp(sd_journal* journal,
172 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800173{
174 int ret = 0;
175 uint64_t timestamp = 0;
176 ret = sd_journal_get_realtime_usec(journal, &timestamp);
177 if (ret < 0)
178 {
179 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
180 << strerror(-ret);
181 return false;
182 }
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500183 entryTimestamp = crow::utility::getDateTime(
184 static_cast<std::time_t>(timestamp / 1000 / 1000));
185 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800186}
187
zhanghch058d1b46d2021-04-01 11:18:24 +0800188static bool getSkipParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
189 const crow::Request& req, uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700190{
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700191 boost::urls::query_params_view::iterator it = req.urlParams.find("$skip");
James Feist5a7e8772020-07-22 09:08:38 -0700192 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700193 {
James Feist5a7e8772020-07-22 09:08:38 -0700194 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500195 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700196 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
197 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700198 {
199
zhanghch058d1b46d2021-04-01 11:18:24 +0800200 messages::queryParameterValueTypeError(
201 asyncResp->res, std::string(skipParam), "$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700202 return false;
203 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700204 }
205 return true;
206}
207
Ed Tanous271584a2019-07-09 16:24:22 -0700208static constexpr const uint64_t maxEntriesPerPage = 1000;
zhanghch058d1b46d2021-04-01 11:18:24 +0800209static bool getTopParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
210 const crow::Request& req, uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700211{
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700212 boost::urls::query_params_view::iterator it = req.urlParams.find("$top");
James Feist5a7e8772020-07-22 09:08:38 -0700213 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700214 {
James Feist5a7e8772020-07-22 09:08:38 -0700215 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500216 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700217 top = std::strtoul(topParam.c_str(), &ptr, 10);
218 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700219 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800220 messages::queryParameterValueTypeError(
221 asyncResp->res, std::string(topParam), "$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700222 return false;
223 }
Ed Tanous271584a2019-07-09 16:24:22 -0700224 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700225 {
226
227 messages::queryParameterOutOfRange(
zhanghch058d1b46d2021-04-01 11:18:24 +0800228 asyncResp->res, std::to_string(top), "$top",
Jason M. Bills16428a12018-11-02 12:42:29 -0700229 "1-" + std::to_string(maxEntriesPerPage));
230 return false;
231 }
232 }
233 return true;
234}
235
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700236inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
237 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700238{
239 int ret = 0;
240 static uint64_t prevTs = 0;
241 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700242 if (firstEntry)
243 {
244 prevTs = 0;
245 }
246
Jason M. Bills16428a12018-11-02 12:42:29 -0700247 // Get the entry timestamp
248 uint64_t curTs = 0;
249 ret = sd_journal_get_realtime_usec(journal, &curTs);
250 if (ret < 0)
251 {
252 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
253 << strerror(-ret);
254 return false;
255 }
256 // If the timestamp isn't unique, increment the index
257 if (curTs == prevTs)
258 {
259 index++;
260 }
261 else
262 {
263 // Otherwise, reset it
264 index = 0;
265 }
266 // Save the timestamp
267 prevTs = curTs;
268
269 entryID = std::to_string(curTs);
270 if (index > 0)
271 {
272 entryID += "_" + std::to_string(index);
273 }
274 return true;
275}
276
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500277static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700278 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700279{
Ed Tanous271584a2019-07-09 16:24:22 -0700280 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700281 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700282 if (firstEntry)
283 {
284 prevTs = 0;
285 }
286
Jason M. Bills95820182019-04-22 16:25:34 -0700287 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700288 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700289 std::tm timeStruct = {};
290 std::istringstream entryStream(logEntry);
291 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
292 {
293 curTs = std::mktime(&timeStruct);
294 }
295 // If the timestamp isn't unique, increment the index
296 if (curTs == prevTs)
297 {
298 index++;
299 }
300 else
301 {
302 // Otherwise, reset it
303 index = 0;
304 }
305 // Save the timestamp
306 prevTs = curTs;
307
308 entryID = std::to_string(curTs);
309 if (index > 0)
310 {
311 entryID += "_" + std::to_string(index);
312 }
313 return true;
314}
315
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700316inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800317 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
318 const std::string& entryID, uint64_t& timestamp,
319 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700320{
321 if (entryID.empty())
322 {
323 return false;
324 }
325 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800326 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700327
Ed Tanous81ce6092020-12-17 16:54:55 +0000328 auto underscorePos = tsStr.find('_');
Jason M. Bills16428a12018-11-02 12:42:29 -0700329 if (underscorePos != tsStr.npos)
330 {
331 // Timestamp has an index
332 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800333 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700334 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700335 auto [ptr, ec] = std::from_chars(
336 indexStr.data(), indexStr.data() + indexStr.size(), index);
337 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700338 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800339 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 return false;
341 }
342 }
343 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700344 auto [ptr, ec] =
345 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
346 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700347 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800348 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700349 return false;
350 }
351 return true;
352}
353
Jason M. Bills95820182019-04-22 16:25:34 -0700354static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500355 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700356{
357 static const std::filesystem::path redfishLogDir = "/var/log";
358 static const std::string redfishLogFilename = "redfish";
359
360 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500361 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700362 std::filesystem::directory_iterator(redfishLogDir))
363 {
364 // If we find a redfish log file, save the path
365 std::string filename = dirEnt.path().filename();
366 if (boost::starts_with(filename, redfishLogFilename))
367 {
368 redfishLogFiles.emplace_back(redfishLogDir / filename);
369 }
370 }
371 // As the log files rotate, they are appended with a ".#" that is higher for
372 // the older logs. Since we don't expect more than 10 log files, we
373 // can just sort the list to get them in order from newest to oldest
374 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
375
376 return !redfishLogFiles.empty();
377}
378
zhanghch058d1b46d2021-04-01 11:18:24 +0800379inline void
380 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
381 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500382{
383 std::string dumpPath;
384 if (dumpType == "BMC")
385 {
386 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
387 }
388 else if (dumpType == "System")
389 {
390 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
391 }
392 else
393 {
394 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
395 messages::internalError(asyncResp->res);
396 return;
397 }
398
399 crow::connections::systemBus->async_method_call(
400 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
401 GetManagedObjectsType& resp) {
402 if (ec)
403 {
404 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
405 messages::internalError(asyncResp->res);
406 return;
407 }
408
409 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
410 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500411 std::string dumpEntryPath =
412 "/xyz/openbmc_project/dump/" +
413 std::string(boost::algorithm::to_lower_copy(dumpType)) +
414 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500415
416 for (auto& object : resp)
417 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500418 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500419 {
420 continue;
421 }
422 std::time_t timestamp;
423 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500424 std::string dumpStatus;
425 nlohmann::json thisEntry;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000426
427 std::string entryID = object.first.filename();
428 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500429 {
430 continue;
431 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500432
433 for (auto& interfaceMap : object.second)
434 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500435 if (interfaceMap.first ==
436 "xyz.openbmc_project.Common.Progress")
437 {
438 for (auto& propertyMap : interfaceMap.second)
439 {
440 if (propertyMap.first == "Status")
441 {
442 auto status = std::get_if<std::string>(
443 &propertyMap.second);
444 if (status == nullptr)
445 {
446 messages::internalError(asyncResp->res);
447 break;
448 }
449 dumpStatus = *status;
450 }
451 }
452 }
453 else if (interfaceMap.first ==
454 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500455 {
456
457 for (auto& propertyMap : interfaceMap.second)
458 {
459 if (propertyMap.first == "Size")
460 {
461 auto sizePtr =
462 std::get_if<uint64_t>(&propertyMap.second);
463 if (sizePtr == nullptr)
464 {
465 messages::internalError(asyncResp->res);
466 break;
467 }
468 size = *sizePtr;
469 break;
470 }
471 }
472 }
473 else if (interfaceMap.first ==
474 "xyz.openbmc_project.Time.EpochTime")
475 {
476
477 for (auto& propertyMap : interfaceMap.second)
478 {
479 if (propertyMap.first == "Elapsed")
480 {
481 const uint64_t* usecsTimeStamp =
482 std::get_if<uint64_t>(&propertyMap.second);
483 if (usecsTimeStamp == nullptr)
484 {
485 messages::internalError(asyncResp->res);
486 break;
487 }
488 timestamp =
489 static_cast<std::time_t>(*usecsTimeStamp);
490 break;
491 }
492 }
493 }
494 }
495
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500496 if (dumpStatus != "xyz.openbmc_project.Common.Progress."
497 "OperationStatus.Completed" &&
498 !dumpStatus.empty())
499 {
500 // Dump status is not Complete, no need to enumerate
501 continue;
502 }
503
George Liu647b3cd2021-07-05 12:43:56 +0800504 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500505 thisEntry["@odata.id"] = dumpPath + entryID;
506 thisEntry["Id"] = entryID;
507 thisEntry["EntryType"] = "Event";
508 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
509 thisEntry["Name"] = dumpType + " Dump Entry";
510
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500511 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500512
513 if (dumpType == "BMC")
514 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500515 thisEntry["DiagnosticDataType"] = "Manager";
516 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500517 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
518 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500519 }
520 else if (dumpType == "System")
521 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500522 thisEntry["DiagnosticDataType"] = "OEM";
523 thisEntry["OEMDiagnosticDataType"] = "System";
524 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500525 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
526 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500527 }
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500528 entriesArray.push_back(std::move(thisEntry));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500529 }
530 asyncResp->res.jsonValue["Members@odata.count"] =
531 entriesArray.size();
532 },
533 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
534 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
535}
536
zhanghch058d1b46d2021-04-01 11:18:24 +0800537inline void
538 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
539 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500540{
541 std::string dumpPath;
542 if (dumpType == "BMC")
543 {
544 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
545 }
546 else if (dumpType == "System")
547 {
548 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
549 }
550 else
551 {
552 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
553 messages::internalError(asyncResp->res);
554 return;
555 }
556
557 crow::connections::systemBus->async_method_call(
558 [asyncResp, entryID, dumpPath, dumpType](
559 const boost::system::error_code ec, GetManagedObjectsType& resp) {
560 if (ec)
561 {
562 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
563 messages::internalError(asyncResp->res);
564 return;
565 }
566
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500567 bool foundDumpEntry = false;
568 std::string dumpEntryPath =
569 "/xyz/openbmc_project/dump/" +
570 std::string(boost::algorithm::to_lower_copy(dumpType)) +
571 "/entry/";
572
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500573 for (auto& objectPath : resp)
574 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500575 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500576 {
577 continue;
578 }
579
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500580 foundDumpEntry = true;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500581 std::time_t timestamp;
582 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500583 std::string dumpStatus;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500584
585 for (auto& interfaceMap : objectPath.second)
586 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500587 if (interfaceMap.first ==
588 "xyz.openbmc_project.Common.Progress")
589 {
590 for (auto& propertyMap : interfaceMap.second)
591 {
592 if (propertyMap.first == "Status")
593 {
594 auto status = std::get_if<std::string>(
595 &propertyMap.second);
596 if (status == nullptr)
597 {
598 messages::internalError(asyncResp->res);
599 break;
600 }
601 dumpStatus = *status;
602 }
603 }
604 }
605 else if (interfaceMap.first ==
606 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500607 {
608 for (auto& propertyMap : interfaceMap.second)
609 {
610 if (propertyMap.first == "Size")
611 {
612 auto sizePtr =
613 std::get_if<uint64_t>(&propertyMap.second);
614 if (sizePtr == nullptr)
615 {
616 messages::internalError(asyncResp->res);
617 break;
618 }
619 size = *sizePtr;
620 break;
621 }
622 }
623 }
624 else if (interfaceMap.first ==
625 "xyz.openbmc_project.Time.EpochTime")
626 {
627 for (auto& propertyMap : interfaceMap.second)
628 {
629 if (propertyMap.first == "Elapsed")
630 {
631 const uint64_t* usecsTimeStamp =
632 std::get_if<uint64_t>(&propertyMap.second);
633 if (usecsTimeStamp == nullptr)
634 {
635 messages::internalError(asyncResp->res);
636 break;
637 }
638 timestamp =
639 static_cast<std::time_t>(*usecsTimeStamp);
640 break;
641 }
642 }
643 }
644 }
645
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500646 if (dumpStatus != "xyz.openbmc_project.Common.Progress."
647 "OperationStatus.Completed" &&
648 !dumpStatus.empty())
649 {
650 // Dump status is not Complete
651 // return not found until status is changed to Completed
652 messages::resourceNotFound(asyncResp->res,
653 dumpType + " dump", entryID);
654 return;
655 }
656
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500657 asyncResp->res.jsonValue["@odata.type"] =
George Liu647b3cd2021-07-05 12:43:56 +0800658 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500659 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
660 asyncResp->res.jsonValue["Id"] = entryID;
661 asyncResp->res.jsonValue["EntryType"] = "Event";
662 asyncResp->res.jsonValue["Created"] =
663 crow::utility::getDateTime(timestamp);
664 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
665
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500666 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500667
668 if (dumpType == "BMC")
669 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500670 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
671 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500672 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
673 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500674 }
675 else if (dumpType == "System")
676 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500677 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
678 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500679 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500680 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500681 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
682 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500683 }
684 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500685 if (foundDumpEntry == false)
686 {
687 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
688 messages::internalError(asyncResp->res);
689 return;
690 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500691 },
692 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
693 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
694}
695
zhanghch058d1b46d2021-04-01 11:18:24 +0800696inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800697 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500698 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500699{
George Liu3de8d8b2021-03-22 17:49:39 +0800700 auto respHandler = [asyncResp,
701 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500702 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
703 if (ec)
704 {
George Liu3de8d8b2021-03-22 17:49:39 +0800705 if (ec.value() == EBADR)
706 {
707 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
708 return;
709 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500710 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
711 << ec;
712 messages::internalError(asyncResp->res);
713 return;
714 }
715 };
716 crow::connections::systemBus->async_method_call(
717 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500718 "/xyz/openbmc_project/dump/" +
719 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
720 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500721 "xyz.openbmc_project.Object.Delete", "Delete");
722}
723
zhanghch058d1b46d2021-04-01 11:18:24 +0800724inline void
725 createDumpTaskCallback(const crow::Request& req,
726 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
727 const uint32_t& dumpId, const std::string& dumpPath,
728 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500729{
730 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500731 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500732 boost::system::error_code err, sdbusplus::message::message& m,
733 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000734 if (err)
735 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500736 BMCWEB_LOG_ERROR << "Error in creating a dump";
737 taskData->state = "Cancelled";
738 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000739 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500740 std::vector<std::pair<
741 std::string,
742 std::vector<std::pair<std::string, std::variant<std::string>>>>>
743 interfacesList;
744
745 sdbusplus::message::object_path objPath;
746
747 m.read(objPath, interfacesList);
748
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500749 if (objPath.str ==
750 "/xyz/openbmc_project/dump/" +
751 std::string(boost::algorithm::to_lower_copy(dumpType)) +
752 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500753 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500754 nlohmann::json retMessage = messages::success();
755 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500756
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500757 std::string headerLoc =
758 "Location: " + dumpPath + std::to_string(dumpId);
759 taskData->payload->httpHeaders.emplace_back(
760 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500761
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500762 taskData->state = "Completed";
763 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500764 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500765 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500766 },
767 "type='signal',interface='org.freedesktop.DBus."
768 "ObjectManager',"
769 "member='InterfacesAdded', "
770 "path='/xyz/openbmc_project/dump'");
771
772 task->startTimer(std::chrono::minutes(3));
773 task->populateResp(asyncResp->res);
774 task->payload.emplace(req);
775}
776
zhanghch058d1b46d2021-04-01 11:18:24 +0800777inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
778 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500779{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500780
781 std::string dumpPath;
782 if (dumpType == "BMC")
783 {
784 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
785 }
786 else if (dumpType == "System")
787 {
788 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
789 }
790 else
791 {
792 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
793 messages::internalError(asyncResp->res);
794 return;
795 }
796
797 std::optional<std::string> diagnosticDataType;
798 std::optional<std::string> oemDiagnosticDataType;
799
800 if (!redfish::json_util::readJson(
801 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
802 "OEMDiagnosticDataType", oemDiagnosticDataType))
803 {
804 return;
805 }
806
807 if (dumpType == "System")
808 {
809 if (!oemDiagnosticDataType || !diagnosticDataType)
810 {
811 BMCWEB_LOG_ERROR << "CreateDump action parameter "
812 "'DiagnosticDataType'/"
813 "'OEMDiagnosticDataType' value not found!";
814 messages::actionParameterMissing(
815 asyncResp->res, "CollectDiagnosticData",
816 "DiagnosticDataType & OEMDiagnosticDataType");
817 return;
818 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700819 if ((*oemDiagnosticDataType != "System") ||
820 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500821 {
822 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
823 messages::invalidObject(asyncResp->res,
824 "System Dump creation parameters");
825 return;
826 }
827 }
828 else if (dumpType == "BMC")
829 {
830 if (!diagnosticDataType)
831 {
832 BMCWEB_LOG_ERROR << "CreateDump action parameter "
833 "'DiagnosticDataType' not found!";
834 messages::actionParameterMissing(
835 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
836 return;
837 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700838 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500839 {
840 BMCWEB_LOG_ERROR
841 << "Wrong parameter value passed for 'DiagnosticDataType'";
842 messages::invalidObject(asyncResp->res,
843 "BMC Dump creation parameters");
844 return;
845 }
846 }
847
848 crow::connections::systemBus->async_method_call(
849 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
850 const uint32_t& dumpId) {
851 if (ec)
852 {
853 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
854 messages::internalError(asyncResp->res);
855 return;
856 }
857 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
858
859 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
860 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500861 "xyz.openbmc_project.Dump.Manager",
862 "/xyz/openbmc_project/dump/" +
863 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500864 "xyz.openbmc_project.Dump.Create", "CreateDump");
865}
866
zhanghch058d1b46d2021-04-01 11:18:24 +0800867inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
868 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500869{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500870 std::string dumpTypeLowerCopy =
871 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800872
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500873 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500874 [asyncResp, dumpType](const boost::system::error_code ec,
875 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500876 if (ec)
877 {
878 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
879 messages::internalError(asyncResp->res);
880 return;
881 }
882
883 for (const std::string& path : subTreePaths)
884 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000885 sdbusplus::message::object_path objPath(path);
886 std::string logID = objPath.filename();
887 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500888 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000889 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500890 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000891 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500892 }
893 },
894 "xyz.openbmc_project.ObjectMapper",
895 "/xyz/openbmc_project/object_mapper",
896 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500897 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
898 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
899 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500900}
901
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700902inline static void parseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500903 const std::vector<std::pair<std::string, VariantType>>& params,
904 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700905{
906 for (auto property : params)
907 {
908 if (property.first == "Timestamp")
909 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500910 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500911 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700912 if (value != nullptr)
913 {
914 timestamp = *value;
915 }
916 }
917 else if (property.first == "Filename")
918 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500919 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500920 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700921 if (value != nullptr)
922 {
923 filename = *value;
924 }
925 }
926 else if (property.first == "Log")
927 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500928 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500929 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700930 if (value != nullptr)
931 {
932 logfile = *value;
933 }
934 }
935 }
936}
937
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500938constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700939inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700940{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800941 /**
942 * Functions triggers appropriate requests on DBus
943 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700944 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700945 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700946 .methods(boost::beast::http::verb::get)(
947 [](const crow::Request&,
948 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
949
950 {
951 // Collections don't include the static data added by SubRoute
952 // because it has a duplicate entry for members
953 asyncResp->res.jsonValue["@odata.type"] =
954 "#LogServiceCollection.LogServiceCollection";
955 asyncResp->res.jsonValue["@odata.id"] =
956 "/redfish/v1/Systems/system/LogServices";
957 asyncResp->res.jsonValue["Name"] =
958 "System Log Services Collection";
959 asyncResp->res.jsonValue["Description"] =
960 "Collection of LogServices for this Computer System";
961 nlohmann::json& logServiceArray =
962 asyncResp->res.jsonValue["Members"];
963 logServiceArray = nlohmann::json::array();
964 logServiceArray.push_back(
965 {{"@odata.id",
966 "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500967#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700968 logServiceArray.push_back(
969 {{"@odata.id",
970 "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600971#endif
972
Jason M. Billsd53dd412019-02-12 17:16:22 -0800973#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700974 logServiceArray.push_back(
975 {{"@odata.id",
976 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800977#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700978 asyncResp->res.jsonValue["Members@odata.count"] =
979 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800980
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700981 crow::connections::systemBus->async_method_call(
982 [asyncResp](const boost::system::error_code ec,
983 const std::vector<std::string>& subtreePath) {
984 if (ec)
985 {
986 BMCWEB_LOG_ERROR << ec;
987 return;
988 }
ZhikuiRena3316fc2020-01-29 14:58:08 -0800989
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700990 for (auto& pathStr : subtreePath)
991 {
992 if (pathStr.find("PostCode") != std::string::npos)
993 {
994 nlohmann::json& logServiceArrayLocal =
995 asyncResp->res.jsonValue["Members"];
996 logServiceArrayLocal.push_back(
997 {{"@odata.id", "/redfish/v1/Systems/system/"
998 "LogServices/PostCodes"}});
999 asyncResp->res
1000 .jsonValue["Members@odata.count"] =
1001 logServiceArrayLocal.size();
1002 return;
1003 }
1004 }
1005 },
1006 "xyz.openbmc_project.ObjectMapper",
1007 "/xyz/openbmc_project/object_mapper",
1008 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
1009 0, std::array<const char*, 1>{postCodeIface});
1010 });
1011}
1012
1013inline void requestRoutesEventLogService(App& app)
1014{
1015 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -07001016 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001017 .methods(
1018 boost::beast::http::verb::
1019 get)([](const crow::Request&,
1020 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1021 asyncResp->res.jsonValue["@odata.id"] =
1022 "/redfish/v1/Systems/system/LogServices/EventLog";
1023 asyncResp->res.jsonValue["@odata.type"] =
1024 "#LogService.v1_1_0.LogService";
1025 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1026 asyncResp->res.jsonValue["Description"] =
1027 "System Event Log Service";
1028 asyncResp->res.jsonValue["Id"] = "EventLog";
1029 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301030
1031 std::pair<std::string, std::string> redfishDateTimeOffset =
1032 crow::utility::getDateTimeOffsetNow();
1033
1034 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1035 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1036 redfishDateTimeOffset.second;
1037
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001038 asyncResp->res.jsonValue["Entries"] = {
1039 {"@odata.id",
1040 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1041 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1042
1043 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1044 "Actions/LogService.ClearLog"}};
1045 });
1046}
1047
1048inline void requestRoutesJournalEventLogClear(App& app)
1049{
1050 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1051 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001052 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001053 .methods(boost::beast::http::verb::post)(
1054 [](const crow::Request&,
1055 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1056 // Clear the EventLog by deleting the log files
1057 std::vector<std::filesystem::path> redfishLogFiles;
1058 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001059 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001060 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001061 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001062 std::error_code ec;
1063 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001064 }
1065 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001066
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001067 // Reload rsyslog so it knows to start new log files
1068 crow::connections::systemBus->async_method_call(
1069 [asyncResp](const boost::system::error_code ec) {
1070 if (ec)
1071 {
1072 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1073 << ec;
1074 messages::internalError(asyncResp->res);
1075 return;
1076 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001077
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001078 messages::success(asyncResp->res);
1079 },
1080 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1081 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1082 "rsyslog.service", "replace");
1083 });
1084}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001085
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001086static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001087 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001088 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001089{
Jason M. Bills95820182019-04-22 16:25:34 -07001090 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001091 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001092 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001093 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001094 {
1095 return 1;
1096 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001097 std::string timestamp = logEntry.substr(0, space);
1098 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001099 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001100 if (entryStart == std::string::npos)
1101 {
1102 return 1;
1103 }
1104 std::string_view entry(logEntry);
1105 entry.remove_prefix(entryStart);
1106 // Use split to separate the entry into its fields
1107 std::vector<std::string> logEntryFields;
1108 boost::split(logEntryFields, entry, boost::is_any_of(","),
1109 boost::token_compress_on);
1110 // We need at least a MessageId to be valid
1111 if (logEntryFields.size() < 1)
1112 {
1113 return 1;
1114 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001115 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001116
Jason M. Bills4851d452019-03-28 11:27:48 -07001117 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001118 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001119 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001120
Jason M. Bills4851d452019-03-28 11:27:48 -07001121 std::string msg;
1122 std::string severity;
1123 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001124 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001125 msg = message->message;
1126 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001127 }
1128
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001129 // Get the MessageArgs from the log if there are any
1130 boost::beast::span<std::string> messageArgs;
1131 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001132 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001133 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001134 // If the first string is empty, assume there are no MessageArgs
1135 std::size_t messageArgsSize = 0;
1136 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001137 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001138 messageArgsSize = logEntryFields.size() - 1;
1139 }
1140
Ed Tanous23a21a12020-07-25 04:45:05 +00001141 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001142
1143 // Fill the MessageArgs into the Message
1144 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001145 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001146 {
1147 std::string argStr = "%" + std::to_string(++i);
1148 size_t argPos = msg.find(argStr);
1149 if (argPos != std::string::npos)
1150 {
1151 msg.replace(argPos, argStr.length(), messageArg);
1152 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001153 }
1154 }
1155
Jason M. Bills95820182019-04-22 16:25:34 -07001156 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1157 // format which matches the Redfish format except for the fractional seconds
1158 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001159 std::size_t dot = timestamp.find_first_of('.');
1160 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001161 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001162 {
Jason M. Bills95820182019-04-22 16:25:34 -07001163 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001164 }
1165
1166 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001167 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001168 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001169 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001170 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001171 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001172 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001173 {"Id", logEntryID},
1174 {"Message", std::move(msg)},
1175 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001176 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001177 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001178 {"Severity", std::move(severity)},
1179 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001180 return 0;
1181}
1182
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001183inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001184{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001185 BMCWEB_ROUTE(app,
1186 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001187 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001188 .methods(boost::beast::http::verb::get)(
1189 [](const crow::Request& req,
1190 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1191 uint64_t skip = 0;
1192 uint64_t top = maxEntriesPerPage; // Show max entries by default
1193 if (!getSkipParam(asyncResp, req, skip))
Jason M. Bills95820182019-04-22 16:25:34 -07001194 {
Jason M. Bills95820182019-04-22 16:25:34 -07001195 return;
1196 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001197 if (!getTopParam(asyncResp, req, top))
Jason M. Bills897967d2019-07-29 17:05:30 -07001198 {
Jason M. Bills897967d2019-07-29 17:05:30 -07001199 return;
1200 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001201 // Collections don't include the static data added by SubRoute
1202 // because it has a duplicate entry for members
1203 asyncResp->res.jsonValue["@odata.type"] =
1204 "#LogEntryCollection.LogEntryCollection";
1205 asyncResp->res.jsonValue["@odata.id"] =
1206 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1207 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1208 asyncResp->res.jsonValue["Description"] =
1209 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001210
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001211 nlohmann::json& logEntryArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001212 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001213 logEntryArray = nlohmann::json::array();
1214 // Go through the log files and create a unique ID for each
1215 // entry
1216 std::vector<std::filesystem::path> redfishLogFiles;
1217 getRedfishLogFiles(redfishLogFiles);
1218 uint64_t entryCount = 0;
1219 std::string logEntry;
1220
1221 // Oldest logs are in the last file, so start there and loop
1222 // backwards
1223 for (auto it = redfishLogFiles.rbegin();
1224 it < redfishLogFiles.rend(); it++)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001225 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001226 std::ifstream logStream(*it);
1227 if (!logStream.is_open())
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001228 {
1229 continue;
1230 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001231
1232 // Reset the unique ID on the first entry
1233 bool firstEntry = true;
1234 while (std::getline(logStream, logEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001235 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001236 entryCount++;
1237 // Handle paging using skip (number of entries to skip
1238 // from the start) and top (number of entries to
1239 // display)
1240 if (entryCount <= skip || entryCount > skip + top)
George Liuebd45902020-08-26 14:21:10 +08001241 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001242 continue;
George Liuebd45902020-08-26 14:21:10 +08001243 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001244
1245 std::string idStr;
1246 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
George Liuebd45902020-08-26 14:21:10 +08001247 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001248 continue;
George Liuebd45902020-08-26 14:21:10 +08001249 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001250
1251 if (firstEntry)
1252 {
1253 firstEntry = false;
1254 }
1255
1256 logEntryArray.push_back({});
1257 nlohmann::json& bmcLogEntry = logEntryArray.back();
1258 if (fillEventLogEntryJson(idStr, logEntry,
1259 bmcLogEntry) != 0)
Xiaochao Ma75710de2021-01-21 17:56:02 +08001260 {
1261 messages::internalError(asyncResp->res);
1262 return;
1263 }
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001264 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001265 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001266 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1267 if (skip + top < entryCount)
Ed Tanous271584a2019-07-09 16:24:22 -07001268 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001269 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001270 "/redfish/v1/Systems/system/LogServices/EventLog/"
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001271 "Entries?$skip=" +
1272 std::to_string(skip + top);
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001273 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001274 });
1275}
Chicago Duan336e96c2019-07-15 14:22:08 +08001276
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001277inline void requestRoutesJournalEventLogEntry(App& app)
1278{
1279 BMCWEB_ROUTE(
1280 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001281 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001282 .methods(boost::beast::http::verb::get)(
1283 [](const crow::Request&,
1284 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1285 const std::string& param) {
1286 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001287
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001288 // Go through the log files and check the unique ID for each
1289 // entry to find the target entry
1290 std::vector<std::filesystem::path> redfishLogFiles;
1291 getRedfishLogFiles(redfishLogFiles);
1292 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001293
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001294 // Oldest logs are in the last file, so start there and loop
1295 // backwards
1296 for (auto it = redfishLogFiles.rbegin();
1297 it < redfishLogFiles.rend(); it++)
1298 {
1299 std::ifstream logStream(*it);
1300 if (!logStream.is_open())
1301 {
1302 continue;
1303 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001304
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001305 // Reset the unique ID on the first entry
1306 bool firstEntry = true;
1307 while (std::getline(logStream, logEntry))
1308 {
1309 std::string idStr;
1310 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1311 {
1312 continue;
1313 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001314
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001315 if (firstEntry)
1316 {
1317 firstEntry = false;
1318 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001319
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001320 if (idStr == targetID)
1321 {
1322 if (fillEventLogEntryJson(
1323 idStr, logEntry,
1324 asyncResp->res.jsonValue) != 0)
1325 {
1326 messages::internalError(asyncResp->res);
1327 return;
1328 }
1329 return;
1330 }
1331 }
1332 }
1333 // Requested ID was not found
1334 messages::resourceMissingAtURI(asyncResp->res, targetID);
1335 });
1336}
1337
1338inline void requestRoutesDBusEventLogEntryCollection(App& app)
1339{
1340 BMCWEB_ROUTE(app,
1341 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001342 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001343 .methods(
1344 boost::beast::http::verb::
1345 get)([](const crow::Request&,
1346 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1347 // Collections don't include the static data added by SubRoute
1348 // because it has a duplicate entry for members
1349 asyncResp->res.jsonValue["@odata.type"] =
1350 "#LogEntryCollection.LogEntryCollection";
1351 asyncResp->res.jsonValue["@odata.id"] =
1352 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1353 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1354 asyncResp->res.jsonValue["Description"] =
1355 "Collection of System Event Log Entries";
1356
1357 // DBus implementation of EventLog/Entries
1358 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001359 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001360 [asyncResp](const boost::system::error_code ec,
1361 GetManagedObjectsType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001362 if (ec)
1363 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001364 // TODO Handle for specific error code
1365 BMCWEB_LOG_ERROR
1366 << "getLogEntriesIfaceData resp_handler got error "
1367 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001368 messages::internalError(asyncResp->res);
1369 return;
1370 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001371 nlohmann::json& entriesArray =
1372 asyncResp->res.jsonValue["Members"];
1373 entriesArray = nlohmann::json::array();
1374 for (auto& objectPath : resp)
1375 {
1376 uint32_t* id = nullptr;
1377 std::time_t timestamp{};
1378 std::time_t updateTimestamp{};
1379 std::string* severity = nullptr;
1380 std::string* message = nullptr;
1381 std::string* filePath = nullptr;
1382 bool resolved = false;
1383 for (auto& interfaceMap : objectPath.second)
1384 {
1385 if (interfaceMap.first ==
1386 "xyz.openbmc_project.Logging.Entry")
1387 {
1388 for (auto& propertyMap : interfaceMap.second)
1389 {
1390 if (propertyMap.first == "Id")
1391 {
1392 id = std::get_if<uint32_t>(
1393 &propertyMap.second);
1394 }
1395 else if (propertyMap.first == "Timestamp")
1396 {
1397 const uint64_t* millisTimeStamp =
1398 std::get_if<uint64_t>(
1399 &propertyMap.second);
1400 if (millisTimeStamp != nullptr)
1401 {
1402 timestamp =
1403 crow::utility::getTimestamp(
1404 *millisTimeStamp);
1405 }
1406 }
1407 else if (propertyMap.first ==
1408 "UpdateTimestamp")
1409 {
1410 const uint64_t* millisTimeStamp =
1411 std::get_if<uint64_t>(
1412 &propertyMap.second);
1413 if (millisTimeStamp != nullptr)
1414 {
1415 updateTimestamp =
1416 crow::utility::getTimestamp(
1417 *millisTimeStamp);
1418 }
1419 }
1420 else if (propertyMap.first == "Severity")
1421 {
1422 severity = std::get_if<std::string>(
1423 &propertyMap.second);
1424 }
1425 else if (propertyMap.first == "Message")
1426 {
1427 message = std::get_if<std::string>(
1428 &propertyMap.second);
1429 }
1430 else if (propertyMap.first == "Resolved")
1431 {
1432 bool* resolveptr = std::get_if<bool>(
1433 &propertyMap.second);
1434 if (resolveptr == nullptr)
1435 {
1436 messages::internalError(
1437 asyncResp->res);
1438 return;
1439 }
1440 resolved = *resolveptr;
1441 }
1442 }
1443 if (id == nullptr || message == nullptr ||
1444 severity == nullptr)
1445 {
1446 messages::internalError(asyncResp->res);
1447 return;
1448 }
1449 }
1450 else if (interfaceMap.first ==
1451 "xyz.openbmc_project.Common.FilePath")
1452 {
1453 for (auto& propertyMap : interfaceMap.second)
1454 {
1455 if (propertyMap.first == "Path")
1456 {
1457 filePath = std::get_if<std::string>(
1458 &propertyMap.second);
1459 }
1460 }
1461 }
1462 }
1463 // Object path without the
1464 // xyz.openbmc_project.Logging.Entry interface, ignore
1465 // and continue.
1466 if (id == nullptr || message == nullptr ||
1467 severity == nullptr)
1468 {
1469 continue;
1470 }
1471 entriesArray.push_back({});
1472 nlohmann::json& thisEntry = entriesArray.back();
1473 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1474 thisEntry["@odata.id"] =
1475 "/redfish/v1/Systems/system/"
1476 "LogServices/EventLog/Entries/" +
1477 std::to_string(*id);
1478 thisEntry["Name"] = "System Event Log Entry";
1479 thisEntry["Id"] = std::to_string(*id);
1480 thisEntry["Message"] = *message;
1481 thisEntry["Resolved"] = resolved;
1482 thisEntry["EntryType"] = "Event";
1483 thisEntry["Severity"] =
1484 translateSeverityDbusToRedfish(*severity);
1485 thisEntry["Created"] =
1486 crow::utility::getDateTime(timestamp);
1487 thisEntry["Modified"] =
1488 crow::utility::getDateTime(updateTimestamp);
1489 if (filePath != nullptr)
1490 {
1491 thisEntry["AdditionalDataURI"] =
1492 "/redfish/v1/Systems/system/LogServices/"
1493 "EventLog/"
1494 "Entries/" +
1495 std::to_string(*id) + "/attachment";
1496 }
1497 }
1498 std::sort(entriesArray.begin(), entriesArray.end(),
1499 [](const nlohmann::json& left,
1500 const nlohmann::json& right) {
1501 return (left["Id"] <= right["Id"]);
1502 });
1503 asyncResp->res.jsonValue["Members@odata.count"] =
1504 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001505 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001506 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1507 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1508 });
1509}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001510
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001511inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001512{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001513 BMCWEB_ROUTE(
1514 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001515 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001516 .methods(boost::beast::http::verb::get)(
1517 [](const crow::Request&,
1518 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1519 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001520
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001521 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001522 std::string entryID = param;
1523 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001524
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001525 // DBus implementation of EventLog/Entries
1526 // Make call to Logging Service to find all log entry objects
1527 crow::connections::systemBus->async_method_call(
1528 [asyncResp, entryID](const boost::system::error_code ec,
1529 GetManagedPropertyType& resp) {
1530 if (ec.value() == EBADR)
1531 {
1532 messages::resourceNotFound(
1533 asyncResp->res, "EventLogEntry", entryID);
1534 return;
1535 }
1536 if (ec)
1537 {
1538 BMCWEB_LOG_ERROR << "EventLogEntry (DBus) "
1539 "resp_handler got error "
1540 << ec;
1541 messages::internalError(asyncResp->res);
1542 return;
1543 }
1544 uint32_t* id = nullptr;
1545 std::time_t timestamp{};
1546 std::time_t updateTimestamp{};
1547 std::string* severity = nullptr;
1548 std::string* message = nullptr;
1549 std::string* filePath = nullptr;
1550 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001551
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001552 for (auto& propertyMap : resp)
1553 {
1554 if (propertyMap.first == "Id")
1555 {
1556 id = std::get_if<uint32_t>(&propertyMap.second);
1557 }
1558 else if (propertyMap.first == "Timestamp")
1559 {
1560 const uint64_t* millisTimeStamp =
1561 std::get_if<uint64_t>(&propertyMap.second);
1562 if (millisTimeStamp != nullptr)
1563 {
1564 timestamp = crow::utility::getTimestamp(
1565 *millisTimeStamp);
1566 }
1567 }
1568 else if (propertyMap.first == "UpdateTimestamp")
1569 {
1570 const uint64_t* millisTimeStamp =
1571 std::get_if<uint64_t>(&propertyMap.second);
1572 if (millisTimeStamp != nullptr)
1573 {
1574 updateTimestamp =
1575 crow::utility::getTimestamp(
1576 *millisTimeStamp);
1577 }
1578 }
1579 else if (propertyMap.first == "Severity")
1580 {
1581 severity = std::get_if<std::string>(
1582 &propertyMap.second);
1583 }
1584 else if (propertyMap.first == "Message")
1585 {
1586 message = std::get_if<std::string>(
1587 &propertyMap.second);
1588 }
1589 else if (propertyMap.first == "Resolved")
1590 {
1591 bool* resolveptr =
1592 std::get_if<bool>(&propertyMap.second);
1593 if (resolveptr == nullptr)
1594 {
1595 messages::internalError(asyncResp->res);
1596 return;
1597 }
1598 resolved = *resolveptr;
1599 }
1600 else if (propertyMap.first == "Path")
1601 {
1602 filePath = std::get_if<std::string>(
1603 &propertyMap.second);
1604 }
1605 }
1606 if (id == nullptr || message == nullptr ||
1607 severity == nullptr)
1608 {
1609 messages::internalError(asyncResp->res);
1610 return;
1611 }
1612 asyncResp->res.jsonValue["@odata.type"] =
1613 "#LogEntry.v1_8_0.LogEntry";
1614 asyncResp->res.jsonValue["@odata.id"] =
1615 "/redfish/v1/Systems/system/LogServices/EventLog/"
1616 "Entries/" +
1617 std::to_string(*id);
1618 asyncResp->res.jsonValue["Name"] =
1619 "System Event Log Entry";
1620 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1621 asyncResp->res.jsonValue["Message"] = *message;
1622 asyncResp->res.jsonValue["Resolved"] = resolved;
1623 asyncResp->res.jsonValue["EntryType"] = "Event";
1624 asyncResp->res.jsonValue["Severity"] =
1625 translateSeverityDbusToRedfish(*severity);
1626 asyncResp->res.jsonValue["Created"] =
1627 crow::utility::getDateTime(timestamp);
1628 asyncResp->res.jsonValue["Modified"] =
1629 crow::utility::getDateTime(updateTimestamp);
1630 if (filePath != nullptr)
1631 {
1632 asyncResp->res.jsonValue["AdditionalDataURI"] =
1633 "/redfish/v1/Systems/system/LogServices/"
1634 "EventLog/"
1635 "attachment/" +
1636 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",
1673 std::variant<bool>(*resolved));
1674 });
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
1706 BMCWEB_LOG_ERROR << "EventLogEntry (DBus) doDelete "
1707 "respHandler got error "
1708 << ec;
1709 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{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001727 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/"
1728 "<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001729 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001730 .methods(boost::beast::http::verb::get)(
1731 [](const crow::Request& req,
1732 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1733 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001734
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001735 {
George Liu647b3cd2021-07-05 12:43:56 +08001736 if (!http_helpers::isOctetAccepted(
1737 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001738 {
1739 asyncResp->res.result(
1740 boost::beast::http::status::bad_request);
1741 return;
1742 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001743
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001744 std::string entryID = param;
1745 dbus::utility::escapePathForDbus(entryID);
1746
1747 crow::connections::systemBus->async_method_call(
1748 [asyncResp,
1749 entryID](const boost::system::error_code ec,
1750 const sdbusplus::message::unix_fd& unixfd) {
1751 if (ec.value() == EBADR)
1752 {
1753 messages::resourceNotFound(
1754 asyncResp->res, "EventLogAttachment", entryID);
1755 return;
1756 }
1757 if (ec)
1758 {
1759 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1760 messages::internalError(asyncResp->res);
1761 return;
1762 }
1763
1764 int fd = -1;
1765 fd = dup(unixfd);
1766 if (fd == -1)
1767 {
1768 messages::internalError(asyncResp->res);
1769 return;
1770 }
1771
1772 long long int size = lseek(fd, 0, SEEK_END);
1773 if (size == -1)
1774 {
1775 messages::internalError(asyncResp->res);
1776 return;
1777 }
1778
1779 // Arbitrary max size of 64kb
1780 constexpr int maxFileSize = 65536;
1781 if (size > maxFileSize)
1782 {
1783 BMCWEB_LOG_ERROR
1784 << "File size exceeds maximum allowed size of "
1785 << maxFileSize;
1786 messages::internalError(asyncResp->res);
1787 return;
1788 }
1789 std::vector<char> data(static_cast<size_t>(size));
1790 long long int rc = lseek(fd, 0, SEEK_SET);
1791 if (rc == -1)
1792 {
1793 messages::internalError(asyncResp->res);
1794 return;
1795 }
1796 rc = read(fd, data.data(), data.size());
1797 if ((rc == -1) || (rc != size))
1798 {
1799 messages::internalError(asyncResp->res);
1800 return;
1801 }
1802 close(fd);
1803
1804 std::string_view strData(data.data(), data.size());
1805 std::string output =
1806 crow::utility::base64encode(strData);
1807
1808 asyncResp->res.addHeader("Content-Type",
1809 "application/octet-stream");
1810 asyncResp->res.addHeader("Content-Transfer-Encoding",
1811 "Base64");
1812 asyncResp->res.body() = std::move(output);
1813 },
1814 "xyz.openbmc_project.Logging",
1815 "/xyz/openbmc_project/logging/entry/" + entryID,
1816 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1817 });
1818}
1819
1820inline void requestRoutesBMCLogServiceCollection(App& app)
1821{
1822 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05001823 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001824 .methods(boost::beast::http::verb::get)(
1825 [](const crow::Request&,
1826 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1827 // Collections don't include the static data added by SubRoute
1828 // because it has a duplicate entry for members
1829 asyncResp->res.jsonValue["@odata.type"] =
1830 "#LogServiceCollection.LogServiceCollection";
1831 asyncResp->res.jsonValue["@odata.id"] =
1832 "/redfish/v1/Managers/bmc/LogServices";
1833 asyncResp->res.jsonValue["Name"] =
1834 "Open BMC Log Services Collection";
1835 asyncResp->res.jsonValue["Description"] =
1836 "Collection of LogServices for this Manager";
1837 nlohmann::json& logServiceArray =
1838 asyncResp->res.jsonValue["Members"];
1839 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001840#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001841 logServiceArray.push_back(
1842 {{"@odata.id",
1843 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001844#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001845#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001846 logServiceArray.push_back(
1847 {{"@odata.id",
1848 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001849#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001850 asyncResp->res.jsonValue["Members@odata.count"] =
1851 logServiceArray.size();
1852 });
1853}
Ed Tanous1da66f72018-07-27 16:13:37 -07001854
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001855inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07001856{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001857 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07001858 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001859 .methods(boost::beast::http::verb::get)(
1860 [](const crow::Request&,
1861 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07001862
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001863 {
1864 asyncResp->res.jsonValue["@odata.type"] =
1865 "#LogService.v1_1_0.LogService";
1866 asyncResp->res.jsonValue["@odata.id"] =
1867 "/redfish/v1/Managers/bmc/LogServices/Journal";
1868 asyncResp->res.jsonValue["Name"] =
1869 "Open BMC Journal Log Service";
1870 asyncResp->res.jsonValue["Description"] =
1871 "BMC Journal Log Service";
1872 asyncResp->res.jsonValue["Id"] = "BMC Journal";
1873 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301874
1875 std::pair<std::string, std::string> redfishDateTimeOffset =
1876 crow::utility::getDateTimeOffsetNow();
1877 asyncResp->res.jsonValue["DateTime"] =
1878 redfishDateTimeOffset.first;
1879 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1880 redfishDateTimeOffset.second;
1881
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001882 asyncResp->res.jsonValue["Entries"] = {
1883 {"@odata.id",
1884 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
1885 });
1886}
Jason M. Billse1f26342018-07-18 12:12:00 -07001887
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001888static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1889 sd_journal* journal,
1890 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001891{
1892 // Get the Log Entry contents
1893 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001894
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001895 std::string message;
1896 std::string_view syslogID;
1897 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
1898 if (ret < 0)
1899 {
1900 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
1901 << strerror(-ret);
1902 }
1903 if (!syslogID.empty())
1904 {
1905 message += std::string(syslogID) + ": ";
1906 }
1907
Ed Tanous39e77502019-03-04 17:35:53 -08001908 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001909 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001910 if (ret < 0)
1911 {
1912 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1913 return 1;
1914 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001915 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001916
1917 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001918 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001919 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001920 if (ret < 0)
1921 {
1922 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001923 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001924
1925 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001926 std::string entryTimeStr;
1927 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001928 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001929 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001930 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001931
1932 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001933 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001934 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001935 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1936 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001937 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001938 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001939 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07001940 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06001941 {"Severity", severity <= 2 ? "Critical"
1942 : severity <= 4 ? "Warning"
1943 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001944 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001945 {"Created", std::move(entryTimeStr)}};
1946 return 0;
1947}
1948
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001949inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07001950{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001951 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001952 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001953 .methods(boost::beast::http::verb::get)(
1954 [](const crow::Request& req,
1955 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1956 static constexpr const long maxEntriesPerPage = 1000;
1957 uint64_t skip = 0;
1958 uint64_t top = maxEntriesPerPage; // Show max entries by default
1959 if (!getSkipParam(asyncResp, req, skip))
1960 {
1961 return;
1962 }
1963 if (!getTopParam(asyncResp, req, top))
1964 {
1965 return;
1966 }
1967 // Collections don't include the static data added by SubRoute
1968 // because it has a duplicate entry for members
1969 asyncResp->res.jsonValue["@odata.type"] =
1970 "#LogEntryCollection.LogEntryCollection";
1971 asyncResp->res.jsonValue["@odata.id"] =
1972 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1973 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1974 asyncResp->res.jsonValue["Description"] =
1975 "Collection of BMC Journal Entries";
1976 nlohmann::json& logEntryArray =
1977 asyncResp->res.jsonValue["Members"];
1978 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07001979
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001980 // Go through the journal and use the timestamp to create a
1981 // unique ID for each entry
1982 sd_journal* journalTmp = nullptr;
1983 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1984 if (ret < 0)
1985 {
1986 BMCWEB_LOG_ERROR << "failed to open journal: "
1987 << strerror(-ret);
1988 messages::internalError(asyncResp->res);
1989 return;
1990 }
1991 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
1992 journal(journalTmp, sd_journal_close);
1993 journalTmp = nullptr;
1994 uint64_t entryCount = 0;
1995 // Reset the unique ID on the first entry
1996 bool firstEntry = true;
1997 SD_JOURNAL_FOREACH(journal.get())
1998 {
1999 entryCount++;
2000 // Handle paging using skip (number of entries to skip from
2001 // the start) and top (number of entries to display)
2002 if (entryCount <= skip || entryCount > skip + top)
2003 {
2004 continue;
2005 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002006
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002007 std::string idStr;
2008 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2009 {
2010 continue;
2011 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002012
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002013 if (firstEntry)
2014 {
2015 firstEntry = false;
2016 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07002017
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002018 logEntryArray.push_back({});
2019 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2020 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2021 bmcJournalLogEntry) != 0)
2022 {
2023 messages::internalError(asyncResp->res);
2024 return;
2025 }
2026 }
2027 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2028 if (skip + top < entryCount)
2029 {
2030 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2031 "/redfish/v1/Managers/bmc/LogServices/Journal/"
2032 "Entries?$skip=" +
2033 std::to_string(skip + top);
2034 }
2035 });
2036}
Jason M. Billse1f26342018-07-18 12:12:00 -07002037
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002038inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002039{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002040 BMCWEB_ROUTE(app,
2041 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002042 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002043 .methods(boost::beast::http::verb::get)(
2044 [](const crow::Request&,
2045 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2046 const std::string& entryID) {
2047 // Convert the unique ID back to a timestamp to find the entry
2048 uint64_t ts = 0;
2049 uint64_t index = 0;
2050 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2051 {
2052 return;
2053 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002054
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002055 sd_journal* journalTmp = nullptr;
2056 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2057 if (ret < 0)
2058 {
2059 BMCWEB_LOG_ERROR << "failed to open journal: "
2060 << strerror(-ret);
2061 messages::internalError(asyncResp->res);
2062 return;
2063 }
2064 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2065 journal(journalTmp, sd_journal_close);
2066 journalTmp = nullptr;
2067 // Go to the timestamp in the log and move to the entry at the
2068 // index tracking the unique ID
2069 std::string idStr;
2070 bool firstEntry = true;
2071 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2072 if (ret < 0)
2073 {
2074 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2075 << strerror(-ret);
2076 messages::internalError(asyncResp->res);
2077 return;
2078 }
2079 for (uint64_t i = 0; i <= index; i++)
2080 {
2081 sd_journal_next(journal.get());
2082 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2083 {
2084 messages::internalError(asyncResp->res);
2085 return;
2086 }
2087 if (firstEntry)
2088 {
2089 firstEntry = false;
2090 }
2091 }
2092 // Confirm that the entry ID matches what was requested
2093 if (idStr != entryID)
2094 {
2095 messages::resourceMissingAtURI(asyncResp->res, entryID);
2096 return;
2097 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002098
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002099 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2100 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002101 {
2102 messages::internalError(asyncResp->res);
2103 return;
2104 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002105 });
2106}
2107
2108inline void requestRoutesBMCDumpService(App& app)
2109{
2110 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002111 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002112 .methods(boost::beast::http::verb::get)(
2113 [](const crow::Request&,
2114 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2115 asyncResp->res.jsonValue["@odata.id"] =
2116 "/redfish/v1/Managers/bmc/LogServices/Dump";
2117 asyncResp->res.jsonValue["@odata.type"] =
2118 "#LogService.v1_2_0.LogService";
2119 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2120 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2121 asyncResp->res.jsonValue["Id"] = "Dump";
2122 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302123
2124 std::pair<std::string, std::string> redfishDateTimeOffset =
2125 crow::utility::getDateTimeOffsetNow();
2126 asyncResp->res.jsonValue["DateTime"] =
2127 redfishDateTimeOffset.first;
2128 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2129 redfishDateTimeOffset.second;
2130
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002131 asyncResp->res.jsonValue["Entries"] = {
2132 {"@odata.id",
2133 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2134 asyncResp->res.jsonValue["Actions"] = {
2135 {"#LogService.ClearLog",
2136 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2137 "Actions/LogService.ClearLog"}}},
2138 {"#LogService.CollectDiagnosticData",
2139 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2140 "Actions/LogService.CollectDiagnosticData"}}}};
2141 });
2142}
2143
2144inline void requestRoutesBMCDumpEntryCollection(App& app)
2145{
2146
2147 /**
2148 * Functions triggers appropriate requests on DBus
2149 */
2150 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002151 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002152 .methods(boost::beast::http::verb::get)(
2153 [](const crow::Request&,
2154 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2155 asyncResp->res.jsonValue["@odata.type"] =
2156 "#LogEntryCollection.LogEntryCollection";
2157 asyncResp->res.jsonValue["@odata.id"] =
2158 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2159 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2160 asyncResp->res.jsonValue["Description"] =
2161 "Collection of BMC Dump Entries";
2162
2163 getDumpEntryCollection(asyncResp, "BMC");
2164 });
2165}
2166
2167inline void requestRoutesBMCDumpEntry(App& app)
2168{
2169 BMCWEB_ROUTE(app,
2170 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002171 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002172 .methods(boost::beast::http::verb::get)(
2173 [](const crow::Request&,
2174 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2175 const std::string& param) {
2176 getDumpEntryById(asyncResp, param, "BMC");
2177 });
2178 BMCWEB_ROUTE(app,
2179 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002180 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002181 .methods(boost::beast::http::verb::delete_)(
2182 [](const crow::Request&,
2183 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2184 const std::string& param) {
2185 deleteDumpEntry(asyncResp, param, "bmc");
2186 });
2187}
2188
2189inline void requestRoutesBMCDumpCreate(App& app)
2190{
2191
2192 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2193 "Actions/"
2194 "LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002195 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002196 .methods(boost::beast::http::verb::post)(
2197 [](const crow::Request& req,
2198 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2199 createDump(asyncResp, req, "BMC");
2200 });
2201}
2202
2203inline void requestRoutesBMCDumpClear(App& app)
2204{
2205 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2206 "Actions/"
2207 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002208 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002209 .methods(boost::beast::http::verb::post)(
2210 [](const crow::Request&,
2211 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2212 clearDump(asyncResp, "BMC");
2213 });
2214}
2215
2216inline void requestRoutesSystemDumpService(App& app)
2217{
2218 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002219 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002220 .methods(boost::beast::http::verb::get)(
2221 [](const crow::Request&,
2222 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2223
2224 {
2225 asyncResp->res.jsonValue["@odata.id"] =
2226 "/redfish/v1/Systems/system/LogServices/Dump";
2227 asyncResp->res.jsonValue["@odata.type"] =
2228 "#LogService.v1_2_0.LogService";
2229 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2230 asyncResp->res.jsonValue["Description"] =
2231 "System Dump LogService";
2232 asyncResp->res.jsonValue["Id"] = "Dump";
2233 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302234
2235 std::pair<std::string, std::string> redfishDateTimeOffset =
2236 crow::utility::getDateTimeOffsetNow();
2237 asyncResp->res.jsonValue["DateTime"] =
2238 redfishDateTimeOffset.first;
2239 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2240 redfishDateTimeOffset.second;
2241
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002242 asyncResp->res.jsonValue["Entries"] = {
2243 {"@odata.id",
2244 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2245 asyncResp->res.jsonValue["Actions"] = {
2246 {"#LogService.ClearLog",
2247 {{"target",
2248 "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2249 "LogService.ClearLog"}}},
2250 {"#LogService.CollectDiagnosticData",
2251 {{"target",
2252 "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2253 "LogService.CollectDiagnosticData"}}}};
2254 });
2255}
2256
2257inline void requestRoutesSystemDumpEntryCollection(App& app)
2258{
2259
2260 /**
2261 * Functions triggers appropriate requests on DBus
2262 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002263 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002264 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002265 .methods(boost::beast::http::verb::get)(
2266 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002267 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002268 asyncResp->res.jsonValue["@odata.type"] =
2269 "#LogEntryCollection.LogEntryCollection";
2270 asyncResp->res.jsonValue["@odata.id"] =
2271 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2272 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2273 asyncResp->res.jsonValue["Description"] =
2274 "Collection of System Dump Entries";
2275
2276 getDumpEntryCollection(asyncResp, "System");
2277 });
2278}
2279
2280inline void requestRoutesSystemDumpEntry(App& app)
2281{
2282 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002283 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002284 .privileges(redfish::privileges::getLogEntry)
2285
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002286 .methods(boost::beast::http::verb::get)(
2287 [](const crow::Request&,
2288 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2289 const std::string& param) {
2290 getDumpEntryById(asyncResp, param, "System");
2291 });
2292
2293 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002294 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002295 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002296 .methods(boost::beast::http::verb::delete_)(
2297 [](const crow::Request&,
2298 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2299 const std::string& param) {
2300 deleteDumpEntry(asyncResp, param, "system");
2301 });
2302}
2303
2304inline void requestRoutesSystemDumpCreate(App& app)
2305{
2306 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2307 "Actions/"
2308 "LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002309 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002310 .methods(boost::beast::http::verb::post)(
2311 [](const crow::Request& req,
2312 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2313
2314 { createDump(asyncResp, req, "System"); });
2315}
2316
2317inline void requestRoutesSystemDumpClear(App& app)
2318{
2319 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2320 "Actions/"
2321 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002322 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002323 .methods(boost::beast::http::verb::post)(
2324 [](const crow::Request&,
2325 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2326
2327 { clearDump(asyncResp, "System"); });
2328}
2329
2330inline void requestRoutesCrashdumpService(App& app)
2331{
2332 // Note: Deviated from redfish privilege registry for GET & HEAD
2333 // method for security reasons.
2334 /**
2335 * Functions triggers appropriate requests on DBus
2336 */
2337 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002338 // This is incorrect, should be:
2339 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002340 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002341 .methods(
2342 boost::beast::http::verb::
2343 get)([](const crow::Request&,
2344 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2345 // Copy over the static data to include the entries added by
2346 // SubRoute
2347 asyncResp->res.jsonValue["@odata.id"] =
2348 "/redfish/v1/Systems/system/LogServices/Crashdump";
2349 asyncResp->res.jsonValue["@odata.type"] =
2350 "#LogService.v1_2_0.LogService";
2351 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2352 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2353 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2354 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2355 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302356
2357 std::pair<std::string, std::string> redfishDateTimeOffset =
2358 crow::utility::getDateTimeOffsetNow();
2359 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2360 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2361 redfishDateTimeOffset.second;
2362
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002363 asyncResp->res.jsonValue["Entries"] = {
2364 {"@odata.id",
2365 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2366 asyncResp->res.jsonValue["Actions"] = {
2367 {"#LogService.ClearLog",
2368 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2369 "Actions/LogService.ClearLog"}}},
2370 {"#LogService.CollectDiagnosticData",
2371 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2372 "Actions/LogService.CollectDiagnosticData"}}}};
2373 });
2374}
2375
2376void inline requestRoutesCrashdumpClear(App& app)
2377{
2378 BMCWEB_ROUTE(app,
2379 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2380 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002381 // This is incorrect, should be:
2382 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002383 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002384 .methods(boost::beast::http::verb::post)(
2385 [](const crow::Request&,
2386 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2387 crow::connections::systemBus->async_method_call(
2388 [asyncResp](const boost::system::error_code ec,
2389 const std::string&) {
2390 if (ec)
2391 {
2392 messages::internalError(asyncResp->res);
2393 return;
2394 }
2395 messages::success(asyncResp->res);
2396 },
2397 crashdumpObject, crashdumpPath, deleteAllInterface,
2398 "DeleteAll");
2399 });
2400}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002401
zhanghch058d1b46d2021-04-01 11:18:24 +08002402static void
2403 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2404 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002405{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002406 auto getStoredLogCallback =
2407 [asyncResp, logID, &logEntryJson](
2408 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002409 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002410 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002411 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002412 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2413 if (ec.value() ==
2414 boost::system::linux_error::bad_request_descriptor)
2415 {
2416 messages::resourceNotFound(asyncResp->res, "LogEntry",
2417 logID);
2418 }
2419 else
2420 {
2421 messages::internalError(asyncResp->res);
2422 }
2423 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002424 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002425
Johnathan Mantey043a0532020-03-10 17:15:28 -07002426 std::string timestamp{};
2427 std::string filename{};
2428 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002429 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002430
2431 if (filename.empty() || timestamp.empty())
2432 {
2433 messages::resourceMissingAtURI(asyncResp->res, logID);
2434 return;
2435 }
2436
2437 std::string crashdumpURI =
2438 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2439 logID + "/" + filename;
Ed Tanousd0dbeef2021-07-01 08:46:46 -07002440 logEntryJson = {{"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002441 {"@odata.id", "/redfish/v1/Systems/system/"
2442 "LogServices/Crashdump/Entries/" +
2443 logID},
2444 {"Name", "CPU Crashdump"},
2445 {"Id", logID},
2446 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002447 {"AdditionalDataURI", std::move(crashdumpURI)},
2448 {"DiagnosticDataType", "OEM"},
2449 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002450 {"Created", std::move(timestamp)}};
2451 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002452 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002453 std::move(getStoredLogCallback), crashdumpObject,
2454 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002455 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002456}
2457
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002458inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002459{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002460 // Note: Deviated from redfish privilege registry for GET & HEAD
2461 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002462 /**
2463 * Functions triggers appropriate requests on DBus
2464 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002465 BMCWEB_ROUTE(app,
2466 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002467 // This is incorrect, should be.
2468 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002469 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002470 .methods(
2471 boost::beast::http::verb::
2472 get)([](const crow::Request&,
2473 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2474 // Collections don't include the static data added by SubRoute
2475 // because it has a duplicate entry for members
2476 auto getLogEntriesCallback = [asyncResp](
2477 const boost::system::error_code ec,
2478 const std::vector<std::string>&
2479 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002480 if (ec)
2481 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002482 if (ec.value() !=
2483 boost::system::errc::no_such_file_or_directory)
2484 {
2485 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2486 << ec.message();
2487 messages::internalError(asyncResp->res);
2488 return;
2489 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002490 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002491 asyncResp->res.jsonValue["@odata.type"] =
2492 "#LogEntryCollection.LogEntryCollection";
2493 asyncResp->res.jsonValue["@odata.id"] =
2494 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2495 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2496 asyncResp->res.jsonValue["Description"] =
2497 "Collection of Crashdump Entries";
2498 nlohmann::json& logEntryArray =
2499 asyncResp->res.jsonValue["Members"];
2500 logEntryArray = nlohmann::json::array();
2501 std::vector<std::string> logIDs;
2502 // Get the list of log entries and build up an empty array big
2503 // enough to hold them
2504 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002505 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002506 // Get the log ID
2507 std::size_t lastPos = objpath.rfind('/');
2508 if (lastPos == std::string::npos)
2509 {
2510 continue;
2511 }
2512 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002513
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002514 // Add a space for the log entry to the array
2515 logEntryArray.push_back({});
2516 }
2517 // Now go through and set up async calls to fill in the entries
2518 size_t index = 0;
2519 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002520 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002521 // Add the log entry to the array
2522 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002523 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002524 asyncResp->res.jsonValue["Members@odata.count"] =
2525 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002526 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002527 crow::connections::systemBus->async_method_call(
2528 std::move(getLogEntriesCallback),
2529 "xyz.openbmc_project.ObjectMapper",
2530 "/xyz/openbmc_project/object_mapper",
2531 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2532 std::array<const char*, 1>{crashdumpInterface});
2533 });
2534}
Ed Tanous1da66f72018-07-27 16:13:37 -07002535
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002536inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002537{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002538 // Note: Deviated from redfish privilege registry for GET & HEAD
2539 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002540
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002541 BMCWEB_ROUTE(
2542 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002543 // this is incorrect, should be
2544 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002545 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002546 .methods(boost::beast::http::verb::get)(
2547 [](const crow::Request&,
2548 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2549 const std::string& param) {
2550 const std::string& logID = param;
2551 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2552 });
2553}
Ed Tanous1da66f72018-07-27 16:13:37 -07002554
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002555inline void requestRoutesCrashdumpFile(App& app)
2556{
2557 // Note: Deviated from redfish privilege registry for GET & HEAD
2558 // method for security reasons.
2559 BMCWEB_ROUTE(
2560 app,
2561 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002562 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002563 .methods(boost::beast::http::verb::get)(
2564 [](const crow::Request&,
2565 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2566 const std::string& logID, const std::string& fileName) {
2567 auto getStoredLogCallback =
2568 [asyncResp, logID, fileName](
2569 const boost::system::error_code ec,
2570 const std::vector<std::pair<std::string, VariantType>>&
2571 resp) {
2572 if (ec)
2573 {
2574 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2575 << ec.message();
2576 messages::internalError(asyncResp->res);
2577 return;
2578 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002579
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002580 std::string dbusFilename{};
2581 std::string dbusTimestamp{};
2582 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002583
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002584 parseCrashdumpParameters(resp, dbusFilename,
2585 dbusTimestamp, dbusFilepath);
2586
2587 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2588 dbusFilepath.empty())
2589 {
2590 messages::resourceMissingAtURI(asyncResp->res,
2591 fileName);
2592 return;
2593 }
2594
2595 // Verify the file name parameter is correct
2596 if (fileName != dbusFilename)
2597 {
2598 messages::resourceMissingAtURI(asyncResp->res,
2599 fileName);
2600 return;
2601 }
2602
2603 if (!std::filesystem::exists(dbusFilepath))
2604 {
2605 messages::resourceMissingAtURI(asyncResp->res,
2606 fileName);
2607 return;
2608 }
2609 std::ifstream ifs(dbusFilepath, std::ios::in |
2610 std::ios::binary |
2611 std::ios::ate);
2612 std::ifstream::pos_type fileSize = ifs.tellg();
2613 if (fileSize < 0)
2614 {
2615 messages::generalError(asyncResp->res);
2616 return;
2617 }
2618 ifs.seekg(0, std::ios::beg);
2619
2620 auto crashData = std::make_unique<char[]>(
2621 static_cast<unsigned int>(fileSize));
2622
2623 ifs.read(crashData.get(), static_cast<int>(fileSize));
2624
2625 // The cast to std::string is intentional in order to
2626 // use the assign() that applies move mechanics
2627 asyncResp->res.body().assign(
2628 static_cast<std::string>(crashData.get()));
2629
2630 // Configure this to be a file download when accessed
2631 // from a browser
2632 asyncResp->res.addHeader("Content-Disposition",
2633 "attachment");
2634 };
2635 crow::connections::systemBus->async_method_call(
2636 std::move(getStoredLogCallback), crashdumpObject,
2637 crashdumpPath + std::string("/") + logID,
2638 "org.freedesktop.DBus.Properties", "GetAll",
2639 crashdumpInterface);
2640 });
2641}
2642
2643inline void requestRoutesCrashdumpCollect(App& app)
2644{
2645 // Note: Deviated from redfish privilege registry for GET & HEAD
2646 // method for security reasons.
2647 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/"
2648 "Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002649 // The below is incorrect; Should be ConfigureManager
2650 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002651 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002652 .methods(
2653 boost::beast::http::verb::
2654 post)([](const crow::Request& req,
2655 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2656 std::string diagnosticDataType;
2657 std::string oemDiagnosticDataType;
2658 if (!redfish::json_util::readJson(
2659 req, asyncResp->res, "DiagnosticDataType",
2660 diagnosticDataType, "OEMDiagnosticDataType",
2661 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002662 {
James Feist46229572020-02-19 15:11:58 -08002663 return;
2664 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002665
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002666 if (diagnosticDataType != "OEM")
2667 {
2668 BMCWEB_LOG_ERROR
2669 << "Only OEM DiagnosticDataType supported for Crashdump";
2670 messages::actionParameterValueFormatError(
2671 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2672 "CollectDiagnosticData");
2673 return;
2674 }
2675
2676 auto collectCrashdumpCallback = [asyncResp, req](
2677 const boost::system::error_code
2678 ec,
2679 const std::string&) {
2680 if (ec)
2681 {
2682 if (ec.value() ==
2683 boost::system::errc::operation_not_supported)
2684 {
2685 messages::resourceInStandby(asyncResp->res);
2686 }
2687 else if (ec.value() ==
2688 boost::system::errc::device_or_resource_busy)
2689 {
2690 messages::serviceTemporarilyUnavailable(asyncResp->res,
2691 "60");
2692 }
2693 else
2694 {
2695 messages::internalError(asyncResp->res);
2696 }
2697 return;
2698 }
2699 std::shared_ptr<task::TaskData> task =
2700 task::TaskData::createTask(
2701 [](boost::system::error_code err,
2702 sdbusplus::message::message&,
2703 const std::shared_ptr<task::TaskData>& taskData) {
2704 if (!err)
2705 {
2706 taskData->messages.emplace_back(
2707 messages::taskCompletedOK(
2708 std::to_string(taskData->index)));
2709 taskData->state = "Completed";
2710 }
2711 return task::completed;
2712 },
2713 "type='signal',interface='org.freedesktop.DBus."
2714 "Properties',"
2715 "member='PropertiesChanged',arg0namespace='com.intel."
2716 "crashdump'");
2717 task->startTimer(std::chrono::minutes(5));
2718 task->populateResp(asyncResp->res);
2719 task->payload.emplace(req);
2720 };
2721
2722 if (oemDiagnosticDataType == "OnDemand")
2723 {
2724 crow::connections::systemBus->async_method_call(
2725 std::move(collectCrashdumpCallback), crashdumpObject,
2726 crashdumpPath, crashdumpOnDemandInterface,
2727 "GenerateOnDemandLog");
2728 }
2729 else if (oemDiagnosticDataType == "Telemetry")
2730 {
2731 crow::connections::systemBus->async_method_call(
2732 std::move(collectCrashdumpCallback), crashdumpObject,
2733 crashdumpPath, crashdumpTelemetryInterface,
2734 "GenerateTelemetryLog");
2735 }
2736 else
2737 {
2738 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2739 << oemDiagnosticDataType;
2740 messages::actionParameterValueFormatError(
2741 asyncResp->res, oemDiagnosticDataType,
2742 "OEMDiagnosticDataType", "CollectDiagnosticData");
2743 return;
2744 }
2745 });
2746}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002747
Andrew Geisslercb92c032018-08-17 07:56:14 -07002748/**
2749 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2750 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002751inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002752{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002753 /**
2754 * Function handles POST method request.
2755 * The Clear Log actions does not require any parameter.The action deletes
2756 * all entries found in the Entries collection for this Log Service.
2757 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002758
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002759 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
2760 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002761 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002762 .methods(boost::beast::http::verb::post)(
2763 [](const crow::Request&,
2764 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2765 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07002766
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002767 // Process response from Logging service.
2768 auto respHandler = [asyncResp](
2769 const boost::system::error_code ec) {
2770 BMCWEB_LOG_DEBUG
2771 << "doClearLog resp_handler callback: Done";
2772 if (ec)
2773 {
2774 // TODO Handle for specific error code
2775 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
2776 << ec;
2777 asyncResp->res.result(
2778 boost::beast::http::status::internal_server_error);
2779 return;
2780 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07002781
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002782 asyncResp->res.result(
2783 boost::beast::http::status::no_content);
2784 };
2785
2786 // Make call to Logging service to request Clear Log
2787 crow::connections::systemBus->async_method_call(
2788 respHandler, "xyz.openbmc_project.Logging",
2789 "/xyz/openbmc_project/logging",
2790 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2791 });
2792}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002793
2794/****************************************************
2795 * Redfish PostCode interfaces
2796 * using DBUS interface: getPostCodesTS
2797 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002798inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002799{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002800 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07002801 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002802 .methods(boost::beast::http::verb::get)(
2803 [](const crow::Request&,
2804 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2805 asyncResp->res.jsonValue = {
2806 {"@odata.id",
2807 "/redfish/v1/Systems/system/LogServices/PostCodes"},
2808 {"@odata.type", "#LogService.v1_1_0.LogService"},
2809 {"Name", "POST Code Log Service"},
2810 {"Description", "POST Code Log Service"},
2811 {"Id", "BIOS POST Code Log"},
2812 {"OverWritePolicy", "WrapsWhenFull"},
2813 {"Entries",
2814 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2815 "PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05302816
2817 std::pair<std::string, std::string> redfishDateTimeOffset =
2818 crow::utility::getDateTimeOffsetNow();
2819 asyncResp->res.jsonValue["DateTime"] =
2820 redfishDateTimeOffset.first;
2821 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2822 redfishDateTimeOffset.second;
2823
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002824 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2825 {"target",
2826 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2827 "Actions/LogService.ClearLog"}};
2828 });
2829}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002830
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002831inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002832{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002833 BMCWEB_ROUTE(app,
2834 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2835 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002836 // The following privilege is incorrect; It should be ConfigureManager
2837 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002838 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002839 .methods(boost::beast::http::verb::post)(
2840 [](const crow::Request&,
2841 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2842 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08002843
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002844 // Make call to post-code service to request clear all
2845 crow::connections::systemBus->async_method_call(
2846 [asyncResp](const boost::system::error_code ec) {
2847 if (ec)
2848 {
2849 // TODO Handle for specific error code
2850 BMCWEB_LOG_ERROR
2851 << "doClearPostCodes resp_handler got error "
2852 << ec;
2853 asyncResp->res.result(boost::beast::http::status::
2854 internal_server_error);
2855 messages::internalError(asyncResp->res);
2856 return;
2857 }
2858 },
2859 "xyz.openbmc_project.State.Boot.PostCode0",
2860 "/xyz/openbmc_project/State/Boot/PostCode0",
2861 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2862 });
2863}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002864
2865static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08002866 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302867 const boost::container::flat_map<
2868 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002869 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2870 const uint64_t skip = 0, const uint64_t top = 0)
2871{
2872 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002873 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05302874 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08002875
2876 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002877 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08002878
2879 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302880 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2881 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002882 {
2883 currentCodeIndex++;
2884 std::string postcodeEntryID =
2885 "B" + std::to_string(bootIndex) + "-" +
2886 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2887
2888 uint64_t usecSinceEpoch = code.first;
2889 uint64_t usTimeOffset = 0;
2890
2891 if (1 == currentCodeIndex)
2892 { // already incremented
2893 firstCodeTimeUs = code.first;
2894 }
2895 else
2896 {
2897 usTimeOffset = code.first - firstCodeTimeUs;
2898 }
2899
2900 // skip if no specific codeIndex is specified and currentCodeIndex does
2901 // not fall between top and skip
2902 if ((codeIndex == 0) &&
2903 (currentCodeIndex <= skip || currentCodeIndex > top))
2904 {
2905 continue;
2906 }
2907
Gunnar Mills4e0453b2020-07-08 14:00:30 -05002908 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08002909 // currentIndex
2910 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2911 {
2912 // This is done for simplicity. 1st entry is needed to calculate
2913 // time offset. To improve efficiency, one can get to the entry
2914 // directly (possibly with flatmap's nth method)
2915 continue;
2916 }
2917
2918 // currentCodeIndex is within top and skip or equal to specified code
2919 // index
2920
2921 // Get the Created time from the timestamp
2922 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05002923 entryTimeStr = crow::utility::getDateTime(
2924 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08002925
2926 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2927 std::ostringstream hexCode;
2928 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302929 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08002930 std::ostringstream timeOffsetStr;
2931 // Set Fixed -Point Notation
2932 timeOffsetStr << std::fixed;
2933 // Set precision to 4 digits
2934 timeOffsetStr << std::setprecision(4);
2935 // Add double to stream
2936 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2937 std::vector<std::string> messageArgs = {
2938 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2939
2940 // Get MessageArgs template from message registry
2941 std::string msg;
2942 if (message != nullptr)
2943 {
2944 msg = message->message;
2945
2946 // fill in this post code value
2947 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002948 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002949 {
2950 std::string argStr = "%" + std::to_string(++i);
2951 size_t argPos = msg.find(argStr);
2952 if (argPos != std::string::npos)
2953 {
2954 msg.replace(argPos, argStr.length(), messageArg);
2955 }
2956 }
2957 }
2958
Tim Leed4342a92020-04-27 11:47:58 +08002959 // Get Severity template from message registry
2960 std::string severity;
2961 if (message != nullptr)
2962 {
2963 severity = message->severity;
2964 }
2965
ZhikuiRena3316fc2020-01-29 14:58:08 -08002966 // add to AsyncResp
2967 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002968 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu647b3cd2021-07-05 12:43:56 +08002969 bmcLogEntry = {{"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Gunnar Mills743e9a12020-10-26 12:44:53 -05002970 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2971 "PostCodes/Entries/" +
2972 postcodeEntryID},
2973 {"Name", "POST Code Log Entry"},
2974 {"Id", postcodeEntryID},
2975 {"Message", std::move(msg)},
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05302976 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
Gunnar Mills743e9a12020-10-26 12:44:53 -05002977 {"MessageArgs", std::move(messageArgs)},
2978 {"EntryType", "Event"},
2979 {"Severity", std::move(severity)},
2980 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08002981 if (!std::get<std::vector<uint8_t>>(code.second).empty())
2982 {
2983 bmcLogEntry["AdditionalDataURI"] =
2984 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
2985 postcodeEntryID + "/attachment";
2986 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08002987 }
2988}
2989
zhanghch058d1b46d2021-04-01 11:18:24 +08002990static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002991 const uint16_t bootIndex,
2992 const uint64_t codeIndex)
2993{
2994 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302995 [aResp, bootIndex,
2996 codeIndex](const boost::system::error_code ec,
2997 const boost::container::flat_map<
2998 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2999 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003000 if (ec)
3001 {
3002 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3003 messages::internalError(aResp->res);
3004 return;
3005 }
3006
3007 // skip the empty postcode boots
3008 if (postcode.empty())
3009 {
3010 return;
3011 }
3012
3013 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3014
3015 aResp->res.jsonValue["Members@odata.count"] =
3016 aResp->res.jsonValue["Members"].size();
3017 },
Jonathan Doman15124762021-01-07 17:54:17 -08003018 "xyz.openbmc_project.State.Boot.PostCode0",
3019 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003020 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3021 bootIndex);
3022}
3023
zhanghch058d1b46d2021-04-01 11:18:24 +08003024static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003025 const uint16_t bootIndex,
3026 const uint16_t bootCount,
3027 const uint64_t entryCount, const uint64_t skip,
3028 const uint64_t top)
3029{
3030 crow::connections::systemBus->async_method_call(
3031 [aResp, bootIndex, bootCount, entryCount, skip,
3032 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303033 const boost::container::flat_map<
3034 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3035 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003036 if (ec)
3037 {
3038 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3039 messages::internalError(aResp->res);
3040 return;
3041 }
3042
3043 uint64_t endCount = entryCount;
3044 if (!postcode.empty())
3045 {
3046 endCount = entryCount + postcode.size();
3047
3048 if ((skip < endCount) && ((top + skip) > entryCount))
3049 {
3050 uint64_t thisBootSkip =
3051 std::max(skip, entryCount) - entryCount;
3052 uint64_t thisBootTop =
3053 std::min(top + skip, endCount) - entryCount;
3054
3055 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3056 thisBootSkip, thisBootTop);
3057 }
3058 aResp->res.jsonValue["Members@odata.count"] = endCount;
3059 }
3060
3061 // continue to previous bootIndex
3062 if (bootIndex < bootCount)
3063 {
3064 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3065 bootCount, endCount, skip, top);
3066 }
3067 else
3068 {
3069 aResp->res.jsonValue["Members@odata.nextLink"] =
3070 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3071 "Entries?$skip=" +
3072 std::to_string(skip + top);
3073 }
3074 },
Jonathan Doman15124762021-01-07 17:54:17 -08003075 "xyz.openbmc_project.State.Boot.PostCode0",
3076 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003077 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3078 bootIndex);
3079}
3080
zhanghch058d1b46d2021-04-01 11:18:24 +08003081static void
3082 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3083 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003084{
3085 uint64_t entryCount = 0;
3086 crow::connections::systemBus->async_method_call(
3087 [aResp, entryCount, skip,
3088 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003089 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003090 if (ec)
3091 {
3092 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3093 messages::internalError(aResp->res);
3094 return;
3095 }
3096 auto pVal = std::get_if<uint16_t>(&bootCount);
3097 if (pVal)
3098 {
3099 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3100 }
3101 else
3102 {
3103 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3104 }
3105 },
Jonathan Doman15124762021-01-07 17:54:17 -08003106 "xyz.openbmc_project.State.Boot.PostCode0",
3107 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003108 "org.freedesktop.DBus.Properties", "Get",
3109 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3110}
3111
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003112inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003113{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003114 BMCWEB_ROUTE(app,
3115 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003116 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003117 .methods(boost::beast::http::verb::get)(
3118 [](const crow::Request& req,
3119 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3120 asyncResp->res.jsonValue["@odata.type"] =
3121 "#LogEntryCollection.LogEntryCollection";
3122 asyncResp->res.jsonValue["@odata.id"] =
3123 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3124 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3125 asyncResp->res.jsonValue["Description"] =
3126 "Collection of POST Code Log Entries";
3127 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3128 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003129
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003130 uint64_t skip = 0;
3131 uint64_t top = maxEntriesPerPage; // Show max entries by default
3132 if (!getSkipParam(asyncResp, req, skip))
3133 {
3134 return;
3135 }
3136 if (!getTopParam(asyncResp, req, top))
3137 {
3138 return;
3139 }
3140 getCurrentBootNumber(asyncResp, skip, top);
3141 });
3142}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003143
George Liu647b3cd2021-07-05 12:43:56 +08003144/**
3145 * @brief Parse post code ID and get the current value and index value
3146 * eg: postCodeID=B1-2, currentValue=1, index=2
3147 *
3148 * @param[in] postCodeID Post Code ID
3149 * @param[out] currentValue Current value
3150 * @param[out] index Index value
3151 *
3152 * @return bool true if the parsing is successful, false the parsing fails
3153 */
3154inline static bool parsePostCode(const std::string& postCodeID,
3155 uint64_t& currentValue, uint16_t& index)
3156{
3157 std::vector<std::string> split;
3158 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3159 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3160 {
3161 return false;
3162 }
3163
3164 const char* start = split[0].data() + 1;
3165 const char* end = split[0].data() + split[0].size();
3166 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3167
3168 if (ptrIndex != end || ecIndex != std::errc())
3169 {
3170 return false;
3171 }
3172
3173 start = split[1].data();
3174 end = split[1].data() + split[1].size();
3175 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3176 if (ptrValue != end || ecValue != std::errc())
3177 {
3178 return false;
3179 }
3180
3181 return true;
3182}
3183
3184inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3185{
3186 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/"
3187 "Entries/<str>/attachment/")
3188 .privileges(redfish::privileges::getLogEntry)
3189 .methods(boost::beast::http::verb::get)(
3190 [](const crow::Request& req,
3191 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3192 const std::string& postCodeID) {
3193 if (!http_helpers::isOctetAccepted(
3194 req.getHeaderValue("Accept")))
3195 {
3196 asyncResp->res.result(
3197 boost::beast::http::status::bad_request);
3198 return;
3199 }
3200
3201 uint64_t currentValue = 0;
3202 uint16_t index = 0;
3203 if (!parsePostCode(postCodeID, currentValue, index))
3204 {
3205 messages::resourceNotFound(asyncResp->res, "LogEntry",
3206 postCodeID);
3207 return;
3208 }
3209
3210 crow::connections::systemBus->async_method_call(
3211 [asyncResp, postCodeID, currentValue](
3212 const boost::system::error_code ec,
3213 const std::vector<std::tuple<
3214 uint64_t, std::vector<uint8_t>>>& postcodes) {
3215 if (ec.value() == EBADR)
3216 {
3217 messages::resourceNotFound(asyncResp->res,
3218 "LogEntry", postCodeID);
3219 return;
3220 }
3221 if (ec)
3222 {
3223 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3224 messages::internalError(asyncResp->res);
3225 return;
3226 }
3227
3228 size_t value = static_cast<size_t>(currentValue) - 1;
3229 if (value == std::string::npos ||
3230 postcodes.size() < currentValue)
3231 {
3232 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3233 messages::resourceNotFound(asyncResp->res,
3234 "LogEntry", postCodeID);
3235 return;
3236 }
3237
3238 auto& [tID, code] = postcodes[value];
3239 if (code.empty())
3240 {
3241 BMCWEB_LOG_INFO << "No found post code data";
3242 messages::resourceNotFound(asyncResp->res,
3243 "LogEntry", postCodeID);
3244 return;
3245 }
3246
3247 std::string_view strData(
3248 reinterpret_cast<const char*>(code.data()),
3249 code.size());
3250
3251 asyncResp->res.addHeader("Content-Type",
3252 "application/octet-stream");
3253 asyncResp->res.addHeader("Content-Transfer-Encoding",
3254 "Base64");
3255 asyncResp->res.body() =
3256 crow::utility::base64encode(strData);
3257 },
3258 "xyz.openbmc_project.State.Boot.PostCode0",
3259 "/xyz/openbmc_project/State/Boot/PostCode0",
3260 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3261 index);
3262 });
3263}
3264
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003265inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003266{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003267 BMCWEB_ROUTE(
3268 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003269 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003270 .methods(boost::beast::http::verb::get)(
3271 [](const crow::Request&,
3272 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3273 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003274 uint16_t bootIndex = 0;
3275 uint64_t codeIndex = 0;
3276 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003277 {
3278 // Requested ID was not found
3279 messages::resourceMissingAtURI(asyncResp->res, targetID);
3280 return;
3281 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003282 if (bootIndex == 0 || codeIndex == 0)
3283 {
3284 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3285 << targetID;
3286 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003287
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003288 asyncResp->res.jsonValue["@odata.type"] =
3289 "#LogEntry.v1_4_0.LogEntry";
3290 asyncResp->res.jsonValue["@odata.id"] =
3291 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3292 "Entries";
3293 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3294 asyncResp->res.jsonValue["Description"] =
3295 "Collection of POST Code Log Entries";
3296 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3297 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003298
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003299 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3300 });
3301}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003302
Ed Tanous1da66f72018-07-27 16:13:37 -07003303} // namespace redfish