blob: 12ec64a8802e8178fc389a9106857c39eec7fe85 [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{
James Feist5a7e8772020-07-22 09:08:38 -0700191 boost::urls::url_view::params_type::iterator it =
192 req.urlParams.find("$skip");
193 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700194 {
James Feist5a7e8772020-07-22 09:08:38 -0700195 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500196 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700197 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
198 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700199 {
200
zhanghch058d1b46d2021-04-01 11:18:24 +0800201 messages::queryParameterValueTypeError(
202 asyncResp->res, std::string(skipParam), "$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700203 return false;
204 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700205 }
206 return true;
207}
208
Ed Tanous271584a2019-07-09 16:24:22 -0700209static constexpr const uint64_t maxEntriesPerPage = 1000;
zhanghch058d1b46d2021-04-01 11:18:24 +0800210static bool getTopParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
211 const crow::Request& req, uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700212{
James Feist5a7e8772020-07-22 09:08:38 -0700213 boost::urls::url_view::params_type::iterator it =
214 req.urlParams.find("$top");
215 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700216 {
James Feist5a7e8772020-07-22 09:08:38 -0700217 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500218 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700219 top = std::strtoul(topParam.c_str(), &ptr, 10);
220 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700221 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800222 messages::queryParameterValueTypeError(
223 asyncResp->res, std::string(topParam), "$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700224 return false;
225 }
Ed Tanous271584a2019-07-09 16:24:22 -0700226 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700227 {
228
229 messages::queryParameterOutOfRange(
zhanghch058d1b46d2021-04-01 11:18:24 +0800230 asyncResp->res, std::to_string(top), "$top",
Jason M. Bills16428a12018-11-02 12:42:29 -0700231 "1-" + std::to_string(maxEntriesPerPage));
232 return false;
233 }
234 }
235 return true;
236}
237
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700238inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
239 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700240{
241 int ret = 0;
242 static uint64_t prevTs = 0;
243 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700244 if (firstEntry)
245 {
246 prevTs = 0;
247 }
248
Jason M. Bills16428a12018-11-02 12:42:29 -0700249 // Get the entry timestamp
250 uint64_t curTs = 0;
251 ret = sd_journal_get_realtime_usec(journal, &curTs);
252 if (ret < 0)
253 {
254 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
255 << strerror(-ret);
256 return false;
257 }
258 // If the timestamp isn't unique, increment the index
259 if (curTs == prevTs)
260 {
261 index++;
262 }
263 else
264 {
265 // Otherwise, reset it
266 index = 0;
267 }
268 // Save the timestamp
269 prevTs = curTs;
270
271 entryID = std::to_string(curTs);
272 if (index > 0)
273 {
274 entryID += "_" + std::to_string(index);
275 }
276 return true;
277}
278
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500279static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700280 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700281{
Ed Tanous271584a2019-07-09 16:24:22 -0700282 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700283 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700284 if (firstEntry)
285 {
286 prevTs = 0;
287 }
288
Jason M. Bills95820182019-04-22 16:25:34 -0700289 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700290 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700291 std::tm timeStruct = {};
292 std::istringstream entryStream(logEntry);
293 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
294 {
295 curTs = std::mktime(&timeStruct);
296 }
297 // If the timestamp isn't unique, increment the index
298 if (curTs == prevTs)
299 {
300 index++;
301 }
302 else
303 {
304 // Otherwise, reset it
305 index = 0;
306 }
307 // Save the timestamp
308 prevTs = curTs;
309
310 entryID = std::to_string(curTs);
311 if (index > 0)
312 {
313 entryID += "_" + std::to_string(index);
314 }
315 return true;
316}
317
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700318inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800319 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
320 const std::string& entryID, uint64_t& timestamp,
321 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700322{
323 if (entryID.empty())
324 {
325 return false;
326 }
327 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800328 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700329
Ed Tanous81ce6092020-12-17 16:54:55 +0000330 auto underscorePos = tsStr.find('_');
Jason M. Bills16428a12018-11-02 12:42:29 -0700331 if (underscorePos != tsStr.npos)
332 {
333 // Timestamp has an index
334 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800335 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700336 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700337 auto [ptr, ec] = std::from_chars(
338 indexStr.data(), indexStr.data() + indexStr.size(), index);
339 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700340 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800341 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700342 return false;
343 }
344 }
345 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700346 auto [ptr, ec] =
347 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
348 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700349 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800350 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700351 return false;
352 }
353 return true;
354}
355
Jason M. Bills95820182019-04-22 16:25:34 -0700356static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500357 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700358{
359 static const std::filesystem::path redfishLogDir = "/var/log";
360 static const std::string redfishLogFilename = "redfish";
361
362 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500363 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700364 std::filesystem::directory_iterator(redfishLogDir))
365 {
366 // If we find a redfish log file, save the path
367 std::string filename = dirEnt.path().filename();
368 if (boost::starts_with(filename, redfishLogFilename))
369 {
370 redfishLogFiles.emplace_back(redfishLogDir / filename);
371 }
372 }
373 // As the log files rotate, they are appended with a ".#" that is higher for
374 // the older logs. Since we don't expect more than 10 log files, we
375 // can just sort the list to get them in order from newest to oldest
376 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
377
378 return !redfishLogFiles.empty();
379}
380
zhanghch058d1b46d2021-04-01 11:18:24 +0800381inline void
382 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
383 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500384{
385 std::string dumpPath;
386 if (dumpType == "BMC")
387 {
388 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
389 }
390 else if (dumpType == "System")
391 {
392 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
393 }
394 else
395 {
396 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
397 messages::internalError(asyncResp->res);
398 return;
399 }
400
401 crow::connections::systemBus->async_method_call(
402 [asyncResp, dumpPath, dumpType](const boost::system::error_code ec,
403 GetManagedObjectsType& resp) {
404 if (ec)
405 {
406 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
407 messages::internalError(asyncResp->res);
408 return;
409 }
410
411 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
412 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500413 std::string dumpEntryPath =
414 "/xyz/openbmc_project/dump/" +
415 std::string(boost::algorithm::to_lower_copy(dumpType)) +
416 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500417
418 for (auto& object : resp)
419 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500420 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500421 {
422 continue;
423 }
424 std::time_t timestamp;
425 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500426 std::string dumpStatus;
427 nlohmann::json thisEntry;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000428
429 std::string entryID = object.first.filename();
430 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500431 {
432 continue;
433 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500434
435 for (auto& interfaceMap : object.second)
436 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500437 if (interfaceMap.first ==
438 "xyz.openbmc_project.Common.Progress")
439 {
440 for (auto& propertyMap : interfaceMap.second)
441 {
442 if (propertyMap.first == "Status")
443 {
444 auto status = std::get_if<std::string>(
445 &propertyMap.second);
446 if (status == nullptr)
447 {
448 messages::internalError(asyncResp->res);
449 break;
450 }
451 dumpStatus = *status;
452 }
453 }
454 }
455 else if (interfaceMap.first ==
456 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500457 {
458
459 for (auto& propertyMap : interfaceMap.second)
460 {
461 if (propertyMap.first == "Size")
462 {
463 auto sizePtr =
464 std::get_if<uint64_t>(&propertyMap.second);
465 if (sizePtr == nullptr)
466 {
467 messages::internalError(asyncResp->res);
468 break;
469 }
470 size = *sizePtr;
471 break;
472 }
473 }
474 }
475 else if (interfaceMap.first ==
476 "xyz.openbmc_project.Time.EpochTime")
477 {
478
479 for (auto& propertyMap : interfaceMap.second)
480 {
481 if (propertyMap.first == "Elapsed")
482 {
483 const uint64_t* usecsTimeStamp =
484 std::get_if<uint64_t>(&propertyMap.second);
485 if (usecsTimeStamp == nullptr)
486 {
487 messages::internalError(asyncResp->res);
488 break;
489 }
490 timestamp =
491 static_cast<std::time_t>(*usecsTimeStamp);
492 break;
493 }
494 }
495 }
496 }
497
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500498 if (dumpStatus != "xyz.openbmc_project.Common.Progress."
499 "OperationStatus.Completed" &&
500 !dumpStatus.empty())
501 {
502 // Dump status is not Complete, no need to enumerate
503 continue;
504 }
505
George Liu647b3cd2021-07-05 12:43:56 +0800506 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500507 thisEntry["@odata.id"] = dumpPath + entryID;
508 thisEntry["Id"] = entryID;
509 thisEntry["EntryType"] = "Event";
510 thisEntry["Created"] = crow::utility::getDateTime(timestamp);
511 thisEntry["Name"] = dumpType + " Dump Entry";
512
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500513 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500514
515 if (dumpType == "BMC")
516 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500517 thisEntry["DiagnosticDataType"] = "Manager";
518 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500519 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
520 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500521 }
522 else if (dumpType == "System")
523 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500524 thisEntry["DiagnosticDataType"] = "OEM";
525 thisEntry["OEMDiagnosticDataType"] = "System";
526 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500527 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
528 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500529 }
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500530 entriesArray.push_back(std::move(thisEntry));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500531 }
532 asyncResp->res.jsonValue["Members@odata.count"] =
533 entriesArray.size();
534 },
535 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
536 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
537}
538
zhanghch058d1b46d2021-04-01 11:18:24 +0800539inline void
540 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
541 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500542{
543 std::string dumpPath;
544 if (dumpType == "BMC")
545 {
546 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
547 }
548 else if (dumpType == "System")
549 {
550 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
551 }
552 else
553 {
554 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
555 messages::internalError(asyncResp->res);
556 return;
557 }
558
559 crow::connections::systemBus->async_method_call(
560 [asyncResp, entryID, dumpPath, dumpType](
561 const boost::system::error_code ec, GetManagedObjectsType& resp) {
562 if (ec)
563 {
564 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
565 messages::internalError(asyncResp->res);
566 return;
567 }
568
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500569 bool foundDumpEntry = false;
570 std::string dumpEntryPath =
571 "/xyz/openbmc_project/dump/" +
572 std::string(boost::algorithm::to_lower_copy(dumpType)) +
573 "/entry/";
574
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500575 for (auto& objectPath : resp)
576 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500577 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500578 {
579 continue;
580 }
581
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500582 foundDumpEntry = true;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500583 std::time_t timestamp;
584 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500585 std::string dumpStatus;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500586
587 for (auto& interfaceMap : objectPath.second)
588 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500589 if (interfaceMap.first ==
590 "xyz.openbmc_project.Common.Progress")
591 {
592 for (auto& propertyMap : interfaceMap.second)
593 {
594 if (propertyMap.first == "Status")
595 {
596 auto status = std::get_if<std::string>(
597 &propertyMap.second);
598 if (status == nullptr)
599 {
600 messages::internalError(asyncResp->res);
601 break;
602 }
603 dumpStatus = *status;
604 }
605 }
606 }
607 else if (interfaceMap.first ==
608 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500609 {
610 for (auto& propertyMap : interfaceMap.second)
611 {
612 if (propertyMap.first == "Size")
613 {
614 auto sizePtr =
615 std::get_if<uint64_t>(&propertyMap.second);
616 if (sizePtr == nullptr)
617 {
618 messages::internalError(asyncResp->res);
619 break;
620 }
621 size = *sizePtr;
622 break;
623 }
624 }
625 }
626 else if (interfaceMap.first ==
627 "xyz.openbmc_project.Time.EpochTime")
628 {
629 for (auto& propertyMap : interfaceMap.second)
630 {
631 if (propertyMap.first == "Elapsed")
632 {
633 const uint64_t* usecsTimeStamp =
634 std::get_if<uint64_t>(&propertyMap.second);
635 if (usecsTimeStamp == nullptr)
636 {
637 messages::internalError(asyncResp->res);
638 break;
639 }
640 timestamp =
641 static_cast<std::time_t>(*usecsTimeStamp);
642 break;
643 }
644 }
645 }
646 }
647
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500648 if (dumpStatus != "xyz.openbmc_project.Common.Progress."
649 "OperationStatus.Completed" &&
650 !dumpStatus.empty())
651 {
652 // Dump status is not Complete
653 // return not found until status is changed to Completed
654 messages::resourceNotFound(asyncResp->res,
655 dumpType + " dump", entryID);
656 return;
657 }
658
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500659 asyncResp->res.jsonValue["@odata.type"] =
George Liu647b3cd2021-07-05 12:43:56 +0800660 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500661 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
662 asyncResp->res.jsonValue["Id"] = entryID;
663 asyncResp->res.jsonValue["EntryType"] = "Event";
664 asyncResp->res.jsonValue["Created"] =
665 crow::utility::getDateTime(timestamp);
666 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
667
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500668 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500669
670 if (dumpType == "BMC")
671 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500672 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
673 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500674 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
675 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500676 }
677 else if (dumpType == "System")
678 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500679 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
680 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500681 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500682 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500683 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
684 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500685 }
686 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500687 if (foundDumpEntry == false)
688 {
689 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
690 messages::internalError(asyncResp->res);
691 return;
692 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500693 },
694 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
695 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
696}
697
zhanghch058d1b46d2021-04-01 11:18:24 +0800698inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800699 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500700 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500701{
George Liu3de8d8b2021-03-22 17:49:39 +0800702 auto respHandler = [asyncResp,
703 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500704 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
705 if (ec)
706 {
George Liu3de8d8b2021-03-22 17:49:39 +0800707 if (ec.value() == EBADR)
708 {
709 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
710 return;
711 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500712 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
713 << ec;
714 messages::internalError(asyncResp->res);
715 return;
716 }
717 };
718 crow::connections::systemBus->async_method_call(
719 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500720 "/xyz/openbmc_project/dump/" +
721 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
722 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500723 "xyz.openbmc_project.Object.Delete", "Delete");
724}
725
zhanghch058d1b46d2021-04-01 11:18:24 +0800726inline void
727 createDumpTaskCallback(const crow::Request& req,
728 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
729 const uint32_t& dumpId, const std::string& dumpPath,
730 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500731{
732 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500733 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500734 boost::system::error_code err, sdbusplus::message::message& m,
735 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000736 if (err)
737 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500738 BMCWEB_LOG_ERROR << "Error in creating a dump";
739 taskData->state = "Cancelled";
740 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000741 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500742 std::vector<std::pair<
743 std::string,
744 std::vector<std::pair<std::string, std::variant<std::string>>>>>
745 interfacesList;
746
747 sdbusplus::message::object_path objPath;
748
749 m.read(objPath, interfacesList);
750
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500751 if (objPath.str ==
752 "/xyz/openbmc_project/dump/" +
753 std::string(boost::algorithm::to_lower_copy(dumpType)) +
754 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500755 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500756 nlohmann::json retMessage = messages::success();
757 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500758
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500759 std::string headerLoc =
760 "Location: " + dumpPath + std::to_string(dumpId);
761 taskData->payload->httpHeaders.emplace_back(
762 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500763
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500764 taskData->state = "Completed";
765 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500766 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500767 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500768 },
769 "type='signal',interface='org.freedesktop.DBus."
770 "ObjectManager',"
771 "member='InterfacesAdded', "
772 "path='/xyz/openbmc_project/dump'");
773
774 task->startTimer(std::chrono::minutes(3));
775 task->populateResp(asyncResp->res);
776 task->payload.emplace(req);
777}
778
zhanghch058d1b46d2021-04-01 11:18:24 +0800779inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
780 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500781{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500782
783 std::string dumpPath;
784 if (dumpType == "BMC")
785 {
786 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
787 }
788 else if (dumpType == "System")
789 {
790 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
791 }
792 else
793 {
794 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
795 messages::internalError(asyncResp->res);
796 return;
797 }
798
799 std::optional<std::string> diagnosticDataType;
800 std::optional<std::string> oemDiagnosticDataType;
801
802 if (!redfish::json_util::readJson(
803 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
804 "OEMDiagnosticDataType", oemDiagnosticDataType))
805 {
806 return;
807 }
808
809 if (dumpType == "System")
810 {
811 if (!oemDiagnosticDataType || !diagnosticDataType)
812 {
813 BMCWEB_LOG_ERROR << "CreateDump action parameter "
814 "'DiagnosticDataType'/"
815 "'OEMDiagnosticDataType' value not found!";
816 messages::actionParameterMissing(
817 asyncResp->res, "CollectDiagnosticData",
818 "DiagnosticDataType & OEMDiagnosticDataType");
819 return;
820 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700821 if ((*oemDiagnosticDataType != "System") ||
822 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500823 {
824 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
825 messages::invalidObject(asyncResp->res,
826 "System Dump creation parameters");
827 return;
828 }
829 }
830 else if (dumpType == "BMC")
831 {
832 if (!diagnosticDataType)
833 {
834 BMCWEB_LOG_ERROR << "CreateDump action parameter "
835 "'DiagnosticDataType' not found!";
836 messages::actionParameterMissing(
837 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
838 return;
839 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700840 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500841 {
842 BMCWEB_LOG_ERROR
843 << "Wrong parameter value passed for 'DiagnosticDataType'";
844 messages::invalidObject(asyncResp->res,
845 "BMC Dump creation parameters");
846 return;
847 }
848 }
849
850 crow::connections::systemBus->async_method_call(
851 [asyncResp, req, dumpPath, dumpType](const boost::system::error_code ec,
852 const uint32_t& dumpId) {
853 if (ec)
854 {
855 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
856 messages::internalError(asyncResp->res);
857 return;
858 }
859 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
860
861 createDumpTaskCallback(req, asyncResp, dumpId, dumpPath, dumpType);
862 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500863 "xyz.openbmc_project.Dump.Manager",
864 "/xyz/openbmc_project/dump/" +
865 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500866 "xyz.openbmc_project.Dump.Create", "CreateDump");
867}
868
zhanghch058d1b46d2021-04-01 11:18:24 +0800869inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
870 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500871{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500872 std::string dumpTypeLowerCopy =
873 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800874
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500875 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500876 [asyncResp, dumpType](const boost::system::error_code ec,
877 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500878 if (ec)
879 {
880 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
881 messages::internalError(asyncResp->res);
882 return;
883 }
884
885 for (const std::string& path : subTreePaths)
886 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000887 sdbusplus::message::object_path objPath(path);
888 std::string logID = objPath.filename();
889 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500890 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000891 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500892 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000893 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500894 }
895 },
896 "xyz.openbmc_project.ObjectMapper",
897 "/xyz/openbmc_project/object_mapper",
898 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500899 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
900 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
901 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500902}
903
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700904inline static void parseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500905 const std::vector<std::pair<std::string, VariantType>>& params,
906 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700907{
908 for (auto property : params)
909 {
910 if (property.first == "Timestamp")
911 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500912 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500913 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700914 if (value != nullptr)
915 {
916 timestamp = *value;
917 }
918 }
919 else if (property.first == "Filename")
920 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500921 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500922 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700923 if (value != nullptr)
924 {
925 filename = *value;
926 }
927 }
928 else if (property.first == "Log")
929 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500930 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500931 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700932 if (value != nullptr)
933 {
934 logfile = *value;
935 }
936 }
937 }
938}
939
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500940constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700941inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700942{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800943 /**
944 * Functions triggers appropriate requests on DBus
945 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700946 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700947 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700948 .methods(boost::beast::http::verb::get)(
949 [](const crow::Request&,
950 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
951
952 {
953 // Collections don't include the static data added by SubRoute
954 // because it has a duplicate entry for members
955 asyncResp->res.jsonValue["@odata.type"] =
956 "#LogServiceCollection.LogServiceCollection";
957 asyncResp->res.jsonValue["@odata.id"] =
958 "/redfish/v1/Systems/system/LogServices";
959 asyncResp->res.jsonValue["Name"] =
960 "System Log Services Collection";
961 asyncResp->res.jsonValue["Description"] =
962 "Collection of LogServices for this Computer System";
963 nlohmann::json& logServiceArray =
964 asyncResp->res.jsonValue["Members"];
965 logServiceArray = nlohmann::json::array();
966 logServiceArray.push_back(
967 {{"@odata.id",
968 "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500969#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700970 logServiceArray.push_back(
971 {{"@odata.id",
972 "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600973#endif
974
Jason M. Billsd53dd412019-02-12 17:16:22 -0800975#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700976 logServiceArray.push_back(
977 {{"@odata.id",
978 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800979#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700980 asyncResp->res.jsonValue["Members@odata.count"] =
981 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800982
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700983 crow::connections::systemBus->async_method_call(
984 [asyncResp](const boost::system::error_code ec,
985 const std::vector<std::string>& subtreePath) {
986 if (ec)
987 {
988 BMCWEB_LOG_ERROR << ec;
989 return;
990 }
ZhikuiRena3316fc2020-01-29 14:58:08 -0800991
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700992 for (auto& pathStr : subtreePath)
993 {
994 if (pathStr.find("PostCode") != std::string::npos)
995 {
996 nlohmann::json& logServiceArrayLocal =
997 asyncResp->res.jsonValue["Members"];
998 logServiceArrayLocal.push_back(
999 {{"@odata.id", "/redfish/v1/Systems/system/"
1000 "LogServices/PostCodes"}});
1001 asyncResp->res
1002 .jsonValue["Members@odata.count"] =
1003 logServiceArrayLocal.size();
1004 return;
1005 }
1006 }
1007 },
1008 "xyz.openbmc_project.ObjectMapper",
1009 "/xyz/openbmc_project/object_mapper",
1010 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
1011 0, std::array<const char*, 1>{postCodeIface});
1012 });
1013}
1014
1015inline void requestRoutesEventLogService(App& app)
1016{
1017 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -07001018 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001019 .methods(
1020 boost::beast::http::verb::
1021 get)([](const crow::Request&,
1022 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1023 asyncResp->res.jsonValue["@odata.id"] =
1024 "/redfish/v1/Systems/system/LogServices/EventLog";
1025 asyncResp->res.jsonValue["@odata.type"] =
1026 "#LogService.v1_1_0.LogService";
1027 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1028 asyncResp->res.jsonValue["Description"] =
1029 "System Event Log Service";
1030 asyncResp->res.jsonValue["Id"] = "EventLog";
1031 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301032
1033 std::pair<std::string, std::string> redfishDateTimeOffset =
1034 crow::utility::getDateTimeOffsetNow();
1035
1036 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1037 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1038 redfishDateTimeOffset.second;
1039
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001040 asyncResp->res.jsonValue["Entries"] = {
1041 {"@odata.id",
1042 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1043 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1044
1045 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
1046 "Actions/LogService.ClearLog"}};
1047 });
1048}
1049
1050inline void requestRoutesJournalEventLogClear(App& app)
1051{
1052 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1053 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001054 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001055 .methods(boost::beast::http::verb::post)(
1056 [](const crow::Request&,
1057 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1058 // Clear the EventLog by deleting the log files
1059 std::vector<std::filesystem::path> redfishLogFiles;
1060 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001061 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001062 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001063 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001064 std::error_code ec;
1065 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001066 }
1067 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001068
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001069 // Reload rsyslog so it knows to start new log files
1070 crow::connections::systemBus->async_method_call(
1071 [asyncResp](const boost::system::error_code ec) {
1072 if (ec)
1073 {
1074 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1075 << ec;
1076 messages::internalError(asyncResp->res);
1077 return;
1078 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001079
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001080 messages::success(asyncResp->res);
1081 },
1082 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1083 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1084 "rsyslog.service", "replace");
1085 });
1086}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001087
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001088static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001089 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001090 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001091{
Jason M. Bills95820182019-04-22 16:25:34 -07001092 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001093 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001094 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001095 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001096 {
1097 return 1;
1098 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001099 std::string timestamp = logEntry.substr(0, space);
1100 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001101 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001102 if (entryStart == std::string::npos)
1103 {
1104 return 1;
1105 }
1106 std::string_view entry(logEntry);
1107 entry.remove_prefix(entryStart);
1108 // Use split to separate the entry into its fields
1109 std::vector<std::string> logEntryFields;
1110 boost::split(logEntryFields, entry, boost::is_any_of(","),
1111 boost::token_compress_on);
1112 // We need at least a MessageId to be valid
1113 if (logEntryFields.size() < 1)
1114 {
1115 return 1;
1116 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001117 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001118
Jason M. Bills4851d452019-03-28 11:27:48 -07001119 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001120 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001121 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001122
Jason M. Bills4851d452019-03-28 11:27:48 -07001123 std::string msg;
1124 std::string severity;
1125 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001126 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001127 msg = message->message;
1128 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001129 }
1130
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001131 // Get the MessageArgs from the log if there are any
1132 boost::beast::span<std::string> messageArgs;
1133 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001134 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001135 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001136 // If the first string is empty, assume there are no MessageArgs
1137 std::size_t messageArgsSize = 0;
1138 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001139 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001140 messageArgsSize = logEntryFields.size() - 1;
1141 }
1142
Ed Tanous23a21a12020-07-25 04:45:05 +00001143 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001144
1145 // Fill the MessageArgs into the Message
1146 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001147 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001148 {
1149 std::string argStr = "%" + std::to_string(++i);
1150 size_t argPos = msg.find(argStr);
1151 if (argPos != std::string::npos)
1152 {
1153 msg.replace(argPos, argStr.length(), messageArg);
1154 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001155 }
1156 }
1157
Jason M. Bills95820182019-04-22 16:25:34 -07001158 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1159 // format which matches the Redfish format except for the fractional seconds
1160 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001161 std::size_t dot = timestamp.find_first_of('.');
1162 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001163 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001164 {
Jason M. Bills95820182019-04-22 16:25:34 -07001165 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001166 }
1167
1168 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001169 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001170 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001171 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001172 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001173 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001174 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001175 {"Id", logEntryID},
1176 {"Message", std::move(msg)},
1177 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001178 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001179 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001180 {"Severity", std::move(severity)},
1181 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001182 return 0;
1183}
1184
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001185inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001186{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001187 BMCWEB_ROUTE(app,
1188 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001189 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001190 .methods(boost::beast::http::verb::get)(
1191 [](const crow::Request& req,
1192 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1193 uint64_t skip = 0;
1194 uint64_t top = maxEntriesPerPage; // Show max entries by default
1195 if (!getSkipParam(asyncResp, req, skip))
Jason M. Bills95820182019-04-22 16:25:34 -07001196 {
Jason M. Bills95820182019-04-22 16:25:34 -07001197 return;
1198 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001199 if (!getTopParam(asyncResp, req, top))
Jason M. Bills897967d2019-07-29 17:05:30 -07001200 {
Jason M. Bills897967d2019-07-29 17:05:30 -07001201 return;
1202 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001203 // Collections don't include the static data added by SubRoute
1204 // because it has a duplicate entry for members
1205 asyncResp->res.jsonValue["@odata.type"] =
1206 "#LogEntryCollection.LogEntryCollection";
1207 asyncResp->res.jsonValue["@odata.id"] =
1208 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1209 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1210 asyncResp->res.jsonValue["Description"] =
1211 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001212
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001213 nlohmann::json& logEntryArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001214 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001215 logEntryArray = nlohmann::json::array();
1216 // Go through the log files and create a unique ID for each
1217 // entry
1218 std::vector<std::filesystem::path> redfishLogFiles;
1219 getRedfishLogFiles(redfishLogFiles);
1220 uint64_t entryCount = 0;
1221 std::string logEntry;
1222
1223 // Oldest logs are in the last file, so start there and loop
1224 // backwards
1225 for (auto it = redfishLogFiles.rbegin();
1226 it < redfishLogFiles.rend(); it++)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001227 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001228 std::ifstream logStream(*it);
1229 if (!logStream.is_open())
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001230 {
1231 continue;
1232 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001233
1234 // Reset the unique ID on the first entry
1235 bool firstEntry = true;
1236 while (std::getline(logStream, logEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001237 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001238 entryCount++;
1239 // Handle paging using skip (number of entries to skip
1240 // from the start) and top (number of entries to
1241 // display)
1242 if (entryCount <= skip || entryCount > skip + top)
George Liuebd45902020-08-26 14:21:10 +08001243 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001244 continue;
George Liuebd45902020-08-26 14:21:10 +08001245 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001246
1247 std::string idStr;
1248 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
George Liuebd45902020-08-26 14:21:10 +08001249 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001250 continue;
George Liuebd45902020-08-26 14:21:10 +08001251 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001252
1253 if (firstEntry)
1254 {
1255 firstEntry = false;
1256 }
1257
1258 logEntryArray.push_back({});
1259 nlohmann::json& bmcLogEntry = logEntryArray.back();
1260 if (fillEventLogEntryJson(idStr, logEntry,
1261 bmcLogEntry) != 0)
Xiaochao Ma75710de2021-01-21 17:56:02 +08001262 {
1263 messages::internalError(asyncResp->res);
1264 return;
1265 }
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001266 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001267 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001268 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1269 if (skip + top < entryCount)
Ed Tanous271584a2019-07-09 16:24:22 -07001270 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001271 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001272 "/redfish/v1/Systems/system/LogServices/EventLog/"
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001273 "Entries?$skip=" +
1274 std::to_string(skip + top);
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001275 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001276 });
1277}
Chicago Duan336e96c2019-07-15 14:22:08 +08001278
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001279inline void requestRoutesJournalEventLogEntry(App& app)
1280{
1281 BMCWEB_ROUTE(
1282 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001283 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001284 .methods(boost::beast::http::verb::get)(
1285 [](const crow::Request&,
1286 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1287 const std::string& param) {
1288 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001289
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001290 // Go through the log files and check the unique ID for each
1291 // entry to find the target entry
1292 std::vector<std::filesystem::path> redfishLogFiles;
1293 getRedfishLogFiles(redfishLogFiles);
1294 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001295
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001296 // Oldest logs are in the last file, so start there and loop
1297 // backwards
1298 for (auto it = redfishLogFiles.rbegin();
1299 it < redfishLogFiles.rend(); it++)
1300 {
1301 std::ifstream logStream(*it);
1302 if (!logStream.is_open())
1303 {
1304 continue;
1305 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001306
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001307 // Reset the unique ID on the first entry
1308 bool firstEntry = true;
1309 while (std::getline(logStream, logEntry))
1310 {
1311 std::string idStr;
1312 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1313 {
1314 continue;
1315 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001316
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001317 if (firstEntry)
1318 {
1319 firstEntry = false;
1320 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001321
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001322 if (idStr == targetID)
1323 {
1324 if (fillEventLogEntryJson(
1325 idStr, logEntry,
1326 asyncResp->res.jsonValue) != 0)
1327 {
1328 messages::internalError(asyncResp->res);
1329 return;
1330 }
1331 return;
1332 }
1333 }
1334 }
1335 // Requested ID was not found
1336 messages::resourceMissingAtURI(asyncResp->res, targetID);
1337 });
1338}
1339
1340inline void requestRoutesDBusEventLogEntryCollection(App& app)
1341{
1342 BMCWEB_ROUTE(app,
1343 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001344 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001345 .methods(
1346 boost::beast::http::verb::
1347 get)([](const crow::Request&,
1348 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1349 // Collections don't include the static data added by SubRoute
1350 // because it has a duplicate entry for members
1351 asyncResp->res.jsonValue["@odata.type"] =
1352 "#LogEntryCollection.LogEntryCollection";
1353 asyncResp->res.jsonValue["@odata.id"] =
1354 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1355 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1356 asyncResp->res.jsonValue["Description"] =
1357 "Collection of System Event Log Entries";
1358
1359 // DBus implementation of EventLog/Entries
1360 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001361 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001362 [asyncResp](const boost::system::error_code ec,
1363 GetManagedObjectsType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001364 if (ec)
1365 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001366 // TODO Handle for specific error code
1367 BMCWEB_LOG_ERROR
1368 << "getLogEntriesIfaceData resp_handler got error "
1369 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001370 messages::internalError(asyncResp->res);
1371 return;
1372 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001373 nlohmann::json& entriesArray =
1374 asyncResp->res.jsonValue["Members"];
1375 entriesArray = nlohmann::json::array();
1376 for (auto& objectPath : resp)
1377 {
1378 uint32_t* id = nullptr;
1379 std::time_t timestamp{};
1380 std::time_t updateTimestamp{};
1381 std::string* severity = nullptr;
1382 std::string* message = nullptr;
1383 std::string* filePath = nullptr;
1384 bool resolved = false;
1385 for (auto& interfaceMap : objectPath.second)
1386 {
1387 if (interfaceMap.first ==
1388 "xyz.openbmc_project.Logging.Entry")
1389 {
1390 for (auto& propertyMap : interfaceMap.second)
1391 {
1392 if (propertyMap.first == "Id")
1393 {
1394 id = std::get_if<uint32_t>(
1395 &propertyMap.second);
1396 }
1397 else if (propertyMap.first == "Timestamp")
1398 {
1399 const uint64_t* millisTimeStamp =
1400 std::get_if<uint64_t>(
1401 &propertyMap.second);
1402 if (millisTimeStamp != nullptr)
1403 {
1404 timestamp =
1405 crow::utility::getTimestamp(
1406 *millisTimeStamp);
1407 }
1408 }
1409 else if (propertyMap.first ==
1410 "UpdateTimestamp")
1411 {
1412 const uint64_t* millisTimeStamp =
1413 std::get_if<uint64_t>(
1414 &propertyMap.second);
1415 if (millisTimeStamp != nullptr)
1416 {
1417 updateTimestamp =
1418 crow::utility::getTimestamp(
1419 *millisTimeStamp);
1420 }
1421 }
1422 else if (propertyMap.first == "Severity")
1423 {
1424 severity = std::get_if<std::string>(
1425 &propertyMap.second);
1426 }
1427 else if (propertyMap.first == "Message")
1428 {
1429 message = std::get_if<std::string>(
1430 &propertyMap.second);
1431 }
1432 else if (propertyMap.first == "Resolved")
1433 {
1434 bool* resolveptr = std::get_if<bool>(
1435 &propertyMap.second);
1436 if (resolveptr == nullptr)
1437 {
1438 messages::internalError(
1439 asyncResp->res);
1440 return;
1441 }
1442 resolved = *resolveptr;
1443 }
1444 }
1445 if (id == nullptr || message == nullptr ||
1446 severity == nullptr)
1447 {
1448 messages::internalError(asyncResp->res);
1449 return;
1450 }
1451 }
1452 else if (interfaceMap.first ==
1453 "xyz.openbmc_project.Common.FilePath")
1454 {
1455 for (auto& propertyMap : interfaceMap.second)
1456 {
1457 if (propertyMap.first == "Path")
1458 {
1459 filePath = std::get_if<std::string>(
1460 &propertyMap.second);
1461 }
1462 }
1463 }
1464 }
1465 // Object path without the
1466 // xyz.openbmc_project.Logging.Entry interface, ignore
1467 // and continue.
1468 if (id == nullptr || message == nullptr ||
1469 severity == nullptr)
1470 {
1471 continue;
1472 }
1473 entriesArray.push_back({});
1474 nlohmann::json& thisEntry = entriesArray.back();
1475 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1476 thisEntry["@odata.id"] =
1477 "/redfish/v1/Systems/system/"
1478 "LogServices/EventLog/Entries/" +
1479 std::to_string(*id);
1480 thisEntry["Name"] = "System Event Log Entry";
1481 thisEntry["Id"] = std::to_string(*id);
1482 thisEntry["Message"] = *message;
1483 thisEntry["Resolved"] = resolved;
1484 thisEntry["EntryType"] = "Event";
1485 thisEntry["Severity"] =
1486 translateSeverityDbusToRedfish(*severity);
1487 thisEntry["Created"] =
1488 crow::utility::getDateTime(timestamp);
1489 thisEntry["Modified"] =
1490 crow::utility::getDateTime(updateTimestamp);
1491 if (filePath != nullptr)
1492 {
1493 thisEntry["AdditionalDataURI"] =
1494 "/redfish/v1/Systems/system/LogServices/"
1495 "EventLog/"
1496 "Entries/" +
1497 std::to_string(*id) + "/attachment";
1498 }
1499 }
1500 std::sort(entriesArray.begin(), entriesArray.end(),
1501 [](const nlohmann::json& left,
1502 const nlohmann::json& right) {
1503 return (left["Id"] <= right["Id"]);
1504 });
1505 asyncResp->res.jsonValue["Members@odata.count"] =
1506 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001507 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001508 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1509 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1510 });
1511}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001512
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001513inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001514{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001515 BMCWEB_ROUTE(
1516 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001517 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001518 .methods(boost::beast::http::verb::get)(
1519 [](const crow::Request&,
1520 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1521 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001522
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001523 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001524 std::string entryID = param;
1525 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001526
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001527 // DBus implementation of EventLog/Entries
1528 // Make call to Logging Service to find all log entry objects
1529 crow::connections::systemBus->async_method_call(
1530 [asyncResp, entryID](const boost::system::error_code ec,
1531 GetManagedPropertyType& resp) {
1532 if (ec.value() == EBADR)
1533 {
1534 messages::resourceNotFound(
1535 asyncResp->res, "EventLogEntry", entryID);
1536 return;
1537 }
1538 if (ec)
1539 {
1540 BMCWEB_LOG_ERROR << "EventLogEntry (DBus) "
1541 "resp_handler got error "
1542 << ec;
1543 messages::internalError(asyncResp->res);
1544 return;
1545 }
1546 uint32_t* id = nullptr;
1547 std::time_t timestamp{};
1548 std::time_t updateTimestamp{};
1549 std::string* severity = nullptr;
1550 std::string* message = nullptr;
1551 std::string* filePath = nullptr;
1552 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001553
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001554 for (auto& propertyMap : resp)
1555 {
1556 if (propertyMap.first == "Id")
1557 {
1558 id = std::get_if<uint32_t>(&propertyMap.second);
1559 }
1560 else if (propertyMap.first == "Timestamp")
1561 {
1562 const uint64_t* millisTimeStamp =
1563 std::get_if<uint64_t>(&propertyMap.second);
1564 if (millisTimeStamp != nullptr)
1565 {
1566 timestamp = crow::utility::getTimestamp(
1567 *millisTimeStamp);
1568 }
1569 }
1570 else if (propertyMap.first == "UpdateTimestamp")
1571 {
1572 const uint64_t* millisTimeStamp =
1573 std::get_if<uint64_t>(&propertyMap.second);
1574 if (millisTimeStamp != nullptr)
1575 {
1576 updateTimestamp =
1577 crow::utility::getTimestamp(
1578 *millisTimeStamp);
1579 }
1580 }
1581 else if (propertyMap.first == "Severity")
1582 {
1583 severity = std::get_if<std::string>(
1584 &propertyMap.second);
1585 }
1586 else if (propertyMap.first == "Message")
1587 {
1588 message = std::get_if<std::string>(
1589 &propertyMap.second);
1590 }
1591 else if (propertyMap.first == "Resolved")
1592 {
1593 bool* resolveptr =
1594 std::get_if<bool>(&propertyMap.second);
1595 if (resolveptr == nullptr)
1596 {
1597 messages::internalError(asyncResp->res);
1598 return;
1599 }
1600 resolved = *resolveptr;
1601 }
1602 else if (propertyMap.first == "Path")
1603 {
1604 filePath = std::get_if<std::string>(
1605 &propertyMap.second);
1606 }
1607 }
1608 if (id == nullptr || message == nullptr ||
1609 severity == nullptr)
1610 {
1611 messages::internalError(asyncResp->res);
1612 return;
1613 }
1614 asyncResp->res.jsonValue["@odata.type"] =
1615 "#LogEntry.v1_8_0.LogEntry";
1616 asyncResp->res.jsonValue["@odata.id"] =
1617 "/redfish/v1/Systems/system/LogServices/EventLog/"
1618 "Entries/" +
1619 std::to_string(*id);
1620 asyncResp->res.jsonValue["Name"] =
1621 "System Event Log Entry";
1622 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1623 asyncResp->res.jsonValue["Message"] = *message;
1624 asyncResp->res.jsonValue["Resolved"] = resolved;
1625 asyncResp->res.jsonValue["EntryType"] = "Event";
1626 asyncResp->res.jsonValue["Severity"] =
1627 translateSeverityDbusToRedfish(*severity);
1628 asyncResp->res.jsonValue["Created"] =
1629 crow::utility::getDateTime(timestamp);
1630 asyncResp->res.jsonValue["Modified"] =
1631 crow::utility::getDateTime(updateTimestamp);
1632 if (filePath != nullptr)
1633 {
1634 asyncResp->res.jsonValue["AdditionalDataURI"] =
1635 "/redfish/v1/Systems/system/LogServices/"
1636 "EventLog/"
1637 "attachment/" +
1638 std::to_string(*id);
1639 }
1640 },
1641 "xyz.openbmc_project.Logging",
1642 "/xyz/openbmc_project/logging/entry/" + entryID,
1643 "org.freedesktop.DBus.Properties", "GetAll", "");
1644 });
1645
1646 BMCWEB_ROUTE(
1647 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001648 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001649 .methods(boost::beast::http::verb::patch)(
1650 [](const crow::Request& req,
1651 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1652 const std::string& entryId) {
1653 std::optional<bool> resolved;
1654
1655 if (!json_util::readJson(req, asyncResp->res, "Resolved",
1656 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001657 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001658 return;
1659 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001660 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001661
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001662 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001663 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001664 if (ec)
1665 {
1666 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1667 messages::internalError(asyncResp->res);
1668 return;
1669 }
1670 },
1671 "xyz.openbmc_project.Logging",
1672 "/xyz/openbmc_project/logging/entry/" + entryId,
1673 "org.freedesktop.DBus.Properties", "Set",
1674 "xyz.openbmc_project.Logging.Entry", "Resolved",
1675 std::variant<bool>(*resolved));
1676 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001677
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001678 BMCWEB_ROUTE(
1679 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001680 .privileges(redfish::privileges::deleteLogEntry)
1681
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001682 .methods(boost::beast::http::verb::delete_)(
1683 [](const crow::Request&,
1684 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1685 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001686
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001687 {
1688 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001689
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001690 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001691
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001692 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001693
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001694 // Process response from Logging service.
1695 auto respHandler = [asyncResp, entryID](
1696 const boost::system::error_code ec) {
1697 BMCWEB_LOG_DEBUG
1698 << "EventLogEntry (DBus) doDelete callback: Done";
1699 if (ec)
1700 {
1701 if (ec.value() == EBADR)
1702 {
1703 messages::resourceNotFound(asyncResp->res,
1704 "LogEntry", entryID);
1705 return;
1706 }
1707 // TODO Handle for specific error code
1708 BMCWEB_LOG_ERROR << "EventLogEntry (DBus) doDelete "
1709 "respHandler got error "
1710 << ec;
1711 asyncResp->res.result(
1712 boost::beast::http::status::internal_server_error);
1713 return;
1714 }
1715
1716 asyncResp->res.result(boost::beast::http::status::ok);
1717 };
1718
1719 // Make call to Logging service to request Delete Log
1720 crow::connections::systemBus->async_method_call(
1721 respHandler, "xyz.openbmc_project.Logging",
1722 "/xyz/openbmc_project/logging/entry/" + entryID,
1723 "xyz.openbmc_project.Object.Delete", "Delete");
1724 });
1725}
1726
1727inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001728{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001729 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/"
1730 "<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001731 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001732 .methods(boost::beast::http::verb::get)(
1733 [](const crow::Request& req,
1734 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1735 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001736
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001737 {
George Liu647b3cd2021-07-05 12:43:56 +08001738 if (!http_helpers::isOctetAccepted(
1739 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001740 {
1741 asyncResp->res.result(
1742 boost::beast::http::status::bad_request);
1743 return;
1744 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001745
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001746 std::string entryID = param;
1747 dbus::utility::escapePathForDbus(entryID);
1748
1749 crow::connections::systemBus->async_method_call(
1750 [asyncResp,
1751 entryID](const boost::system::error_code ec,
1752 const sdbusplus::message::unix_fd& unixfd) {
1753 if (ec.value() == EBADR)
1754 {
1755 messages::resourceNotFound(
1756 asyncResp->res, "EventLogAttachment", entryID);
1757 return;
1758 }
1759 if (ec)
1760 {
1761 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1762 messages::internalError(asyncResp->res);
1763 return;
1764 }
1765
1766 int fd = -1;
1767 fd = dup(unixfd);
1768 if (fd == -1)
1769 {
1770 messages::internalError(asyncResp->res);
1771 return;
1772 }
1773
1774 long long int size = lseek(fd, 0, SEEK_END);
1775 if (size == -1)
1776 {
1777 messages::internalError(asyncResp->res);
1778 return;
1779 }
1780
1781 // Arbitrary max size of 64kb
1782 constexpr int maxFileSize = 65536;
1783 if (size > maxFileSize)
1784 {
1785 BMCWEB_LOG_ERROR
1786 << "File size exceeds maximum allowed size of "
1787 << maxFileSize;
1788 messages::internalError(asyncResp->res);
1789 return;
1790 }
1791 std::vector<char> data(static_cast<size_t>(size));
1792 long long int rc = lseek(fd, 0, SEEK_SET);
1793 if (rc == -1)
1794 {
1795 messages::internalError(asyncResp->res);
1796 return;
1797 }
1798 rc = read(fd, data.data(), data.size());
1799 if ((rc == -1) || (rc != size))
1800 {
1801 messages::internalError(asyncResp->res);
1802 return;
1803 }
1804 close(fd);
1805
1806 std::string_view strData(data.data(), data.size());
1807 std::string output =
1808 crow::utility::base64encode(strData);
1809
1810 asyncResp->res.addHeader("Content-Type",
1811 "application/octet-stream");
1812 asyncResp->res.addHeader("Content-Transfer-Encoding",
1813 "Base64");
1814 asyncResp->res.body() = std::move(output);
1815 },
1816 "xyz.openbmc_project.Logging",
1817 "/xyz/openbmc_project/logging/entry/" + entryID,
1818 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1819 });
1820}
1821
1822inline void requestRoutesBMCLogServiceCollection(App& app)
1823{
1824 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05001825 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001826 .methods(boost::beast::http::verb::get)(
1827 [](const crow::Request&,
1828 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1829 // Collections don't include the static data added by SubRoute
1830 // because it has a duplicate entry for members
1831 asyncResp->res.jsonValue["@odata.type"] =
1832 "#LogServiceCollection.LogServiceCollection";
1833 asyncResp->res.jsonValue["@odata.id"] =
1834 "/redfish/v1/Managers/bmc/LogServices";
1835 asyncResp->res.jsonValue["Name"] =
1836 "Open BMC Log Services Collection";
1837 asyncResp->res.jsonValue["Description"] =
1838 "Collection of LogServices for this Manager";
1839 nlohmann::json& logServiceArray =
1840 asyncResp->res.jsonValue["Members"];
1841 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001842#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001843 logServiceArray.push_back(
1844 {{"@odata.id",
1845 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001846#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001847#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001848 logServiceArray.push_back(
1849 {{"@odata.id",
1850 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001851#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001852 asyncResp->res.jsonValue["Members@odata.count"] =
1853 logServiceArray.size();
1854 });
1855}
Ed Tanous1da66f72018-07-27 16:13:37 -07001856
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001857inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07001858{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001859 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07001860 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001861 .methods(boost::beast::http::verb::get)(
1862 [](const crow::Request&,
1863 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07001864
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001865 {
1866 asyncResp->res.jsonValue["@odata.type"] =
1867 "#LogService.v1_1_0.LogService";
1868 asyncResp->res.jsonValue["@odata.id"] =
1869 "/redfish/v1/Managers/bmc/LogServices/Journal";
1870 asyncResp->res.jsonValue["Name"] =
1871 "Open BMC Journal Log Service";
1872 asyncResp->res.jsonValue["Description"] =
1873 "BMC Journal Log Service";
1874 asyncResp->res.jsonValue["Id"] = "BMC Journal";
1875 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301876
1877 std::pair<std::string, std::string> redfishDateTimeOffset =
1878 crow::utility::getDateTimeOffsetNow();
1879 asyncResp->res.jsonValue["DateTime"] =
1880 redfishDateTimeOffset.first;
1881 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1882 redfishDateTimeOffset.second;
1883
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001884 asyncResp->res.jsonValue["Entries"] = {
1885 {"@odata.id",
1886 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
1887 });
1888}
Jason M. Billse1f26342018-07-18 12:12:00 -07001889
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001890static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1891 sd_journal* journal,
1892 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001893{
1894 // Get the Log Entry contents
1895 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001896
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001897 std::string message;
1898 std::string_view syslogID;
1899 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
1900 if (ret < 0)
1901 {
1902 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
1903 << strerror(-ret);
1904 }
1905 if (!syslogID.empty())
1906 {
1907 message += std::string(syslogID) + ": ";
1908 }
1909
Ed Tanous39e77502019-03-04 17:35:53 -08001910 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001911 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001912 if (ret < 0)
1913 {
1914 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1915 return 1;
1916 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001917 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001918
1919 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001920 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001921 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001922 if (ret < 0)
1923 {
1924 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001925 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001926
1927 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001928 std::string entryTimeStr;
1929 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001930 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001931 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001932 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001933
1934 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001935 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001936 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001937 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1938 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001939 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001940 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08001941 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07001942 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06001943 {"Severity", severity <= 2 ? "Critical"
1944 : severity <= 4 ? "Warning"
1945 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001946 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001947 {"Created", std::move(entryTimeStr)}};
1948 return 0;
1949}
1950
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001951inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07001952{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001953 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001954 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001955 .methods(boost::beast::http::verb::get)(
1956 [](const crow::Request& req,
1957 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1958 static constexpr const long maxEntriesPerPage = 1000;
1959 uint64_t skip = 0;
1960 uint64_t top = maxEntriesPerPage; // Show max entries by default
1961 if (!getSkipParam(asyncResp, req, skip))
1962 {
1963 return;
1964 }
1965 if (!getTopParam(asyncResp, req, top))
1966 {
1967 return;
1968 }
1969 // Collections don't include the static data added by SubRoute
1970 // because it has a duplicate entry for members
1971 asyncResp->res.jsonValue["@odata.type"] =
1972 "#LogEntryCollection.LogEntryCollection";
1973 asyncResp->res.jsonValue["@odata.id"] =
1974 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
1975 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1976 asyncResp->res.jsonValue["Description"] =
1977 "Collection of BMC Journal Entries";
1978 nlohmann::json& logEntryArray =
1979 asyncResp->res.jsonValue["Members"];
1980 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07001981
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001982 // Go through the journal and use the timestamp to create a
1983 // unique ID for each entry
1984 sd_journal* journalTmp = nullptr;
1985 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1986 if (ret < 0)
1987 {
1988 BMCWEB_LOG_ERROR << "failed to open journal: "
1989 << strerror(-ret);
1990 messages::internalError(asyncResp->res);
1991 return;
1992 }
1993 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
1994 journal(journalTmp, sd_journal_close);
1995 journalTmp = nullptr;
1996 uint64_t entryCount = 0;
1997 // Reset the unique ID on the first entry
1998 bool firstEntry = true;
1999 SD_JOURNAL_FOREACH(journal.get())
2000 {
2001 entryCount++;
2002 // Handle paging using skip (number of entries to skip from
2003 // the start) and top (number of entries to display)
2004 if (entryCount <= skip || entryCount > skip + top)
2005 {
2006 continue;
2007 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002008
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002009 std::string idStr;
2010 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2011 {
2012 continue;
2013 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002014
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002015 if (firstEntry)
2016 {
2017 firstEntry = false;
2018 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07002019
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002020 logEntryArray.push_back({});
2021 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2022 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2023 bmcJournalLogEntry) != 0)
2024 {
2025 messages::internalError(asyncResp->res);
2026 return;
2027 }
2028 }
2029 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2030 if (skip + top < entryCount)
2031 {
2032 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2033 "/redfish/v1/Managers/bmc/LogServices/Journal/"
2034 "Entries?$skip=" +
2035 std::to_string(skip + top);
2036 }
2037 });
2038}
Jason M. Billse1f26342018-07-18 12:12:00 -07002039
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002040inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002041{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002042 BMCWEB_ROUTE(app,
2043 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002044 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002045 .methods(boost::beast::http::verb::get)(
2046 [](const crow::Request&,
2047 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2048 const std::string& entryID) {
2049 // Convert the unique ID back to a timestamp to find the entry
2050 uint64_t ts = 0;
2051 uint64_t index = 0;
2052 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2053 {
2054 return;
2055 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002056
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002057 sd_journal* journalTmp = nullptr;
2058 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2059 if (ret < 0)
2060 {
2061 BMCWEB_LOG_ERROR << "failed to open journal: "
2062 << strerror(-ret);
2063 messages::internalError(asyncResp->res);
2064 return;
2065 }
2066 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2067 journal(journalTmp, sd_journal_close);
2068 journalTmp = nullptr;
2069 // Go to the timestamp in the log and move to the entry at the
2070 // index tracking the unique ID
2071 std::string idStr;
2072 bool firstEntry = true;
2073 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2074 if (ret < 0)
2075 {
2076 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2077 << strerror(-ret);
2078 messages::internalError(asyncResp->res);
2079 return;
2080 }
2081 for (uint64_t i = 0; i <= index; i++)
2082 {
2083 sd_journal_next(journal.get());
2084 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2085 {
2086 messages::internalError(asyncResp->res);
2087 return;
2088 }
2089 if (firstEntry)
2090 {
2091 firstEntry = false;
2092 }
2093 }
2094 // Confirm that the entry ID matches what was requested
2095 if (idStr != entryID)
2096 {
2097 messages::resourceMissingAtURI(asyncResp->res, entryID);
2098 return;
2099 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002100
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002101 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2102 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002103 {
2104 messages::internalError(asyncResp->res);
2105 return;
2106 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002107 });
2108}
2109
2110inline void requestRoutesBMCDumpService(App& app)
2111{
2112 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002113 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002114 .methods(boost::beast::http::verb::get)(
2115 [](const crow::Request&,
2116 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2117 asyncResp->res.jsonValue["@odata.id"] =
2118 "/redfish/v1/Managers/bmc/LogServices/Dump";
2119 asyncResp->res.jsonValue["@odata.type"] =
2120 "#LogService.v1_2_0.LogService";
2121 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2122 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2123 asyncResp->res.jsonValue["Id"] = "Dump";
2124 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302125
2126 std::pair<std::string, std::string> redfishDateTimeOffset =
2127 crow::utility::getDateTimeOffsetNow();
2128 asyncResp->res.jsonValue["DateTime"] =
2129 redfishDateTimeOffset.first;
2130 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2131 redfishDateTimeOffset.second;
2132
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002133 asyncResp->res.jsonValue["Entries"] = {
2134 {"@odata.id",
2135 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2136 asyncResp->res.jsonValue["Actions"] = {
2137 {"#LogService.ClearLog",
2138 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2139 "Actions/LogService.ClearLog"}}},
2140 {"#LogService.CollectDiagnosticData",
2141 {{"target", "/redfish/v1/Managers/bmc/LogServices/Dump/"
2142 "Actions/LogService.CollectDiagnosticData"}}}};
2143 });
2144}
2145
2146inline void requestRoutesBMCDumpEntryCollection(App& app)
2147{
2148
2149 /**
2150 * Functions triggers appropriate requests on DBus
2151 */
2152 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002153 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002154 .methods(boost::beast::http::verb::get)(
2155 [](const crow::Request&,
2156 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2157 asyncResp->res.jsonValue["@odata.type"] =
2158 "#LogEntryCollection.LogEntryCollection";
2159 asyncResp->res.jsonValue["@odata.id"] =
2160 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2161 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2162 asyncResp->res.jsonValue["Description"] =
2163 "Collection of BMC Dump Entries";
2164
2165 getDumpEntryCollection(asyncResp, "BMC");
2166 });
2167}
2168
2169inline void requestRoutesBMCDumpEntry(App& app)
2170{
2171 BMCWEB_ROUTE(app,
2172 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002173 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002174 .methods(boost::beast::http::verb::get)(
2175 [](const crow::Request&,
2176 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2177 const std::string& param) {
2178 getDumpEntryById(asyncResp, param, "BMC");
2179 });
2180 BMCWEB_ROUTE(app,
2181 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002182 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002183 .methods(boost::beast::http::verb::delete_)(
2184 [](const crow::Request&,
2185 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2186 const std::string& param) {
2187 deleteDumpEntry(asyncResp, param, "bmc");
2188 });
2189}
2190
2191inline void requestRoutesBMCDumpCreate(App& app)
2192{
2193
2194 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2195 "Actions/"
2196 "LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002197 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002198 .methods(boost::beast::http::verb::post)(
2199 [](const crow::Request& req,
2200 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2201 createDump(asyncResp, req, "BMC");
2202 });
2203}
2204
2205inline void requestRoutesBMCDumpClear(App& app)
2206{
2207 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/"
2208 "Actions/"
2209 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002210 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002211 .methods(boost::beast::http::verb::post)(
2212 [](const crow::Request&,
2213 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2214 clearDump(asyncResp, "BMC");
2215 });
2216}
2217
2218inline void requestRoutesSystemDumpService(App& app)
2219{
2220 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002221 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002222 .methods(boost::beast::http::verb::get)(
2223 [](const crow::Request&,
2224 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2225
2226 {
2227 asyncResp->res.jsonValue["@odata.id"] =
2228 "/redfish/v1/Systems/system/LogServices/Dump";
2229 asyncResp->res.jsonValue["@odata.type"] =
2230 "#LogService.v1_2_0.LogService";
2231 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2232 asyncResp->res.jsonValue["Description"] =
2233 "System Dump LogService";
2234 asyncResp->res.jsonValue["Id"] = "Dump";
2235 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302236
2237 std::pair<std::string, std::string> redfishDateTimeOffset =
2238 crow::utility::getDateTimeOffsetNow();
2239 asyncResp->res.jsonValue["DateTime"] =
2240 redfishDateTimeOffset.first;
2241 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2242 redfishDateTimeOffset.second;
2243
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002244 asyncResp->res.jsonValue["Entries"] = {
2245 {"@odata.id",
2246 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2247 asyncResp->res.jsonValue["Actions"] = {
2248 {"#LogService.ClearLog",
2249 {{"target",
2250 "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2251 "LogService.ClearLog"}}},
2252 {"#LogService.CollectDiagnosticData",
2253 {{"target",
2254 "/redfish/v1/Systems/system/LogServices/Dump/Actions/"
2255 "LogService.CollectDiagnosticData"}}}};
2256 });
2257}
2258
2259inline void requestRoutesSystemDumpEntryCollection(App& app)
2260{
2261
2262 /**
2263 * Functions triggers appropriate requests on DBus
2264 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002265 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002266 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002267 .methods(boost::beast::http::verb::get)(
2268 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002269 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002270 asyncResp->res.jsonValue["@odata.type"] =
2271 "#LogEntryCollection.LogEntryCollection";
2272 asyncResp->res.jsonValue["@odata.id"] =
2273 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2274 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2275 asyncResp->res.jsonValue["Description"] =
2276 "Collection of System Dump Entries";
2277
2278 getDumpEntryCollection(asyncResp, "System");
2279 });
2280}
2281
2282inline void requestRoutesSystemDumpEntry(App& app)
2283{
2284 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002285 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002286 .privileges(redfish::privileges::getLogEntry)
2287
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002288 .methods(boost::beast::http::verb::get)(
2289 [](const crow::Request&,
2290 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2291 const std::string& param) {
2292 getDumpEntryById(asyncResp, param, "System");
2293 });
2294
2295 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002296 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002297 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002298 .methods(boost::beast::http::verb::delete_)(
2299 [](const crow::Request&,
2300 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2301 const std::string& param) {
2302 deleteDumpEntry(asyncResp, param, "system");
2303 });
2304}
2305
2306inline void requestRoutesSystemDumpCreate(App& app)
2307{
2308 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2309 "Actions/"
2310 "LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002311 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002312 .methods(boost::beast::http::verb::post)(
2313 [](const crow::Request& req,
2314 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2315
2316 { createDump(asyncResp, req, "System"); });
2317}
2318
2319inline void requestRoutesSystemDumpClear(App& app)
2320{
2321 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/"
2322 "Actions/"
2323 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002324 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002325 .methods(boost::beast::http::verb::post)(
2326 [](const crow::Request&,
2327 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2328
2329 { clearDump(asyncResp, "System"); });
2330}
2331
2332inline void requestRoutesCrashdumpService(App& app)
2333{
2334 // Note: Deviated from redfish privilege registry for GET & HEAD
2335 // method for security reasons.
2336 /**
2337 * Functions triggers appropriate requests on DBus
2338 */
2339 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002340 // This is incorrect, should be:
2341 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002342 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002343 .methods(
2344 boost::beast::http::verb::
2345 get)([](const crow::Request&,
2346 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2347 // Copy over the static data to include the entries added by
2348 // SubRoute
2349 asyncResp->res.jsonValue["@odata.id"] =
2350 "/redfish/v1/Systems/system/LogServices/Crashdump";
2351 asyncResp->res.jsonValue["@odata.type"] =
2352 "#LogService.v1_2_0.LogService";
2353 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2354 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2355 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2356 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2357 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302358
2359 std::pair<std::string, std::string> redfishDateTimeOffset =
2360 crow::utility::getDateTimeOffsetNow();
2361 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2362 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2363 redfishDateTimeOffset.second;
2364
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002365 asyncResp->res.jsonValue["Entries"] = {
2366 {"@odata.id",
2367 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2368 asyncResp->res.jsonValue["Actions"] = {
2369 {"#LogService.ClearLog",
2370 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2371 "Actions/LogService.ClearLog"}}},
2372 {"#LogService.CollectDiagnosticData",
2373 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
2374 "Actions/LogService.CollectDiagnosticData"}}}};
2375 });
2376}
2377
2378void inline requestRoutesCrashdumpClear(App& app)
2379{
2380 BMCWEB_ROUTE(app,
2381 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2382 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002383 // This is incorrect, should be:
2384 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002385 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002386 .methods(boost::beast::http::verb::post)(
2387 [](const crow::Request&,
2388 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2389 crow::connections::systemBus->async_method_call(
2390 [asyncResp](const boost::system::error_code ec,
2391 const std::string&) {
2392 if (ec)
2393 {
2394 messages::internalError(asyncResp->res);
2395 return;
2396 }
2397 messages::success(asyncResp->res);
2398 },
2399 crashdumpObject, crashdumpPath, deleteAllInterface,
2400 "DeleteAll");
2401 });
2402}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002403
zhanghch058d1b46d2021-04-01 11:18:24 +08002404static void
2405 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2406 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002407{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002408 auto getStoredLogCallback =
2409 [asyncResp, logID, &logEntryJson](
2410 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002411 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002412 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002413 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002414 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2415 if (ec.value() ==
2416 boost::system::linux_error::bad_request_descriptor)
2417 {
2418 messages::resourceNotFound(asyncResp->res, "LogEntry",
2419 logID);
2420 }
2421 else
2422 {
2423 messages::internalError(asyncResp->res);
2424 }
2425 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002426 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002427
Johnathan Mantey043a0532020-03-10 17:15:28 -07002428 std::string timestamp{};
2429 std::string filename{};
2430 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002431 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002432
2433 if (filename.empty() || timestamp.empty())
2434 {
2435 messages::resourceMissingAtURI(asyncResp->res, logID);
2436 return;
2437 }
2438
2439 std::string crashdumpURI =
2440 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2441 logID + "/" + filename;
Ed Tanousd0dbeef2021-07-01 08:46:46 -07002442 logEntryJson = {{"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002443 {"@odata.id", "/redfish/v1/Systems/system/"
2444 "LogServices/Crashdump/Entries/" +
2445 logID},
2446 {"Name", "CPU Crashdump"},
2447 {"Id", logID},
2448 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002449 {"AdditionalDataURI", std::move(crashdumpURI)},
2450 {"DiagnosticDataType", "OEM"},
2451 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002452 {"Created", std::move(timestamp)}};
2453 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002454 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002455 std::move(getStoredLogCallback), crashdumpObject,
2456 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002457 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002458}
2459
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002460inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002461{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002462 // Note: Deviated from redfish privilege registry for GET & HEAD
2463 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002464 /**
2465 * Functions triggers appropriate requests on DBus
2466 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002467 BMCWEB_ROUTE(app,
2468 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002469 // This is incorrect, should be.
2470 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002471 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002472 .methods(
2473 boost::beast::http::verb::
2474 get)([](const crow::Request&,
2475 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2476 // Collections don't include the static data added by SubRoute
2477 // because it has a duplicate entry for members
2478 auto getLogEntriesCallback = [asyncResp](
2479 const boost::system::error_code ec,
2480 const std::vector<std::string>&
2481 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002482 if (ec)
2483 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002484 if (ec.value() !=
2485 boost::system::errc::no_such_file_or_directory)
2486 {
2487 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2488 << ec.message();
2489 messages::internalError(asyncResp->res);
2490 return;
2491 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002492 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002493 asyncResp->res.jsonValue["@odata.type"] =
2494 "#LogEntryCollection.LogEntryCollection";
2495 asyncResp->res.jsonValue["@odata.id"] =
2496 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2497 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2498 asyncResp->res.jsonValue["Description"] =
2499 "Collection of Crashdump Entries";
2500 nlohmann::json& logEntryArray =
2501 asyncResp->res.jsonValue["Members"];
2502 logEntryArray = nlohmann::json::array();
2503 std::vector<std::string> logIDs;
2504 // Get the list of log entries and build up an empty array big
2505 // enough to hold them
2506 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002507 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002508 // Get the log ID
2509 std::size_t lastPos = objpath.rfind('/');
2510 if (lastPos == std::string::npos)
2511 {
2512 continue;
2513 }
2514 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002515
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002516 // Add a space for the log entry to the array
2517 logEntryArray.push_back({});
2518 }
2519 // Now go through and set up async calls to fill in the entries
2520 size_t index = 0;
2521 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002522 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002523 // Add the log entry to the array
2524 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002525 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002526 asyncResp->res.jsonValue["Members@odata.count"] =
2527 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002528 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002529 crow::connections::systemBus->async_method_call(
2530 std::move(getLogEntriesCallback),
2531 "xyz.openbmc_project.ObjectMapper",
2532 "/xyz/openbmc_project/object_mapper",
2533 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2534 std::array<const char*, 1>{crashdumpInterface});
2535 });
2536}
Ed Tanous1da66f72018-07-27 16:13:37 -07002537
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002538inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002539{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002540 // Note: Deviated from redfish privilege registry for GET & HEAD
2541 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002542
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002543 BMCWEB_ROUTE(
2544 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002545 // this is incorrect, should be
2546 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002547 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002548 .methods(boost::beast::http::verb::get)(
2549 [](const crow::Request&,
2550 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2551 const std::string& param) {
2552 const std::string& logID = param;
2553 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2554 });
2555}
Ed Tanous1da66f72018-07-27 16:13:37 -07002556
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002557inline void requestRoutesCrashdumpFile(App& app)
2558{
2559 // Note: Deviated from redfish privilege registry for GET & HEAD
2560 // method for security reasons.
2561 BMCWEB_ROUTE(
2562 app,
2563 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002564 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002565 .methods(boost::beast::http::verb::get)(
2566 [](const crow::Request&,
2567 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2568 const std::string& logID, const std::string& fileName) {
2569 auto getStoredLogCallback =
2570 [asyncResp, logID, fileName](
2571 const boost::system::error_code ec,
2572 const std::vector<std::pair<std::string, VariantType>>&
2573 resp) {
2574 if (ec)
2575 {
2576 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2577 << ec.message();
2578 messages::internalError(asyncResp->res);
2579 return;
2580 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002581
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002582 std::string dbusFilename{};
2583 std::string dbusTimestamp{};
2584 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002585
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002586 parseCrashdumpParameters(resp, dbusFilename,
2587 dbusTimestamp, dbusFilepath);
2588
2589 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2590 dbusFilepath.empty())
2591 {
2592 messages::resourceMissingAtURI(asyncResp->res,
2593 fileName);
2594 return;
2595 }
2596
2597 // Verify the file name parameter is correct
2598 if (fileName != dbusFilename)
2599 {
2600 messages::resourceMissingAtURI(asyncResp->res,
2601 fileName);
2602 return;
2603 }
2604
2605 if (!std::filesystem::exists(dbusFilepath))
2606 {
2607 messages::resourceMissingAtURI(asyncResp->res,
2608 fileName);
2609 return;
2610 }
2611 std::ifstream ifs(dbusFilepath, std::ios::in |
2612 std::ios::binary |
2613 std::ios::ate);
2614 std::ifstream::pos_type fileSize = ifs.tellg();
2615 if (fileSize < 0)
2616 {
2617 messages::generalError(asyncResp->res);
2618 return;
2619 }
2620 ifs.seekg(0, std::ios::beg);
2621
2622 auto crashData = std::make_unique<char[]>(
2623 static_cast<unsigned int>(fileSize));
2624
2625 ifs.read(crashData.get(), static_cast<int>(fileSize));
2626
2627 // The cast to std::string is intentional in order to
2628 // use the assign() that applies move mechanics
2629 asyncResp->res.body().assign(
2630 static_cast<std::string>(crashData.get()));
2631
2632 // Configure this to be a file download when accessed
2633 // from a browser
2634 asyncResp->res.addHeader("Content-Disposition",
2635 "attachment");
2636 };
2637 crow::connections::systemBus->async_method_call(
2638 std::move(getStoredLogCallback), crashdumpObject,
2639 crashdumpPath + std::string("/") + logID,
2640 "org.freedesktop.DBus.Properties", "GetAll",
2641 crashdumpInterface);
2642 });
2643}
2644
2645inline void requestRoutesCrashdumpCollect(App& app)
2646{
2647 // Note: Deviated from redfish privilege registry for GET & HEAD
2648 // method for security reasons.
2649 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/"
2650 "Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002651 // The below is incorrect; Should be ConfigureManager
2652 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002653 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002654 .methods(
2655 boost::beast::http::verb::
2656 post)([](const crow::Request& req,
2657 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2658 std::string diagnosticDataType;
2659 std::string oemDiagnosticDataType;
2660 if (!redfish::json_util::readJson(
2661 req, asyncResp->res, "DiagnosticDataType",
2662 diagnosticDataType, "OEMDiagnosticDataType",
2663 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002664 {
James Feist46229572020-02-19 15:11:58 -08002665 return;
2666 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002667
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002668 if (diagnosticDataType != "OEM")
2669 {
2670 BMCWEB_LOG_ERROR
2671 << "Only OEM DiagnosticDataType supported for Crashdump";
2672 messages::actionParameterValueFormatError(
2673 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2674 "CollectDiagnosticData");
2675 return;
2676 }
2677
2678 auto collectCrashdumpCallback = [asyncResp, req](
2679 const boost::system::error_code
2680 ec,
2681 const std::string&) {
2682 if (ec)
2683 {
2684 if (ec.value() ==
2685 boost::system::errc::operation_not_supported)
2686 {
2687 messages::resourceInStandby(asyncResp->res);
2688 }
2689 else if (ec.value() ==
2690 boost::system::errc::device_or_resource_busy)
2691 {
2692 messages::serviceTemporarilyUnavailable(asyncResp->res,
2693 "60");
2694 }
2695 else
2696 {
2697 messages::internalError(asyncResp->res);
2698 }
2699 return;
2700 }
2701 std::shared_ptr<task::TaskData> task =
2702 task::TaskData::createTask(
2703 [](boost::system::error_code err,
2704 sdbusplus::message::message&,
2705 const std::shared_ptr<task::TaskData>& taskData) {
2706 if (!err)
2707 {
2708 taskData->messages.emplace_back(
2709 messages::taskCompletedOK(
2710 std::to_string(taskData->index)));
2711 taskData->state = "Completed";
2712 }
2713 return task::completed;
2714 },
2715 "type='signal',interface='org.freedesktop.DBus."
2716 "Properties',"
2717 "member='PropertiesChanged',arg0namespace='com.intel."
2718 "crashdump'");
2719 task->startTimer(std::chrono::minutes(5));
2720 task->populateResp(asyncResp->res);
2721 task->payload.emplace(req);
2722 };
2723
2724 if (oemDiagnosticDataType == "OnDemand")
2725 {
2726 crow::connections::systemBus->async_method_call(
2727 std::move(collectCrashdumpCallback), crashdumpObject,
2728 crashdumpPath, crashdumpOnDemandInterface,
2729 "GenerateOnDemandLog");
2730 }
2731 else if (oemDiagnosticDataType == "Telemetry")
2732 {
2733 crow::connections::systemBus->async_method_call(
2734 std::move(collectCrashdumpCallback), crashdumpObject,
2735 crashdumpPath, crashdumpTelemetryInterface,
2736 "GenerateTelemetryLog");
2737 }
2738 else
2739 {
2740 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2741 << oemDiagnosticDataType;
2742 messages::actionParameterValueFormatError(
2743 asyncResp->res, oemDiagnosticDataType,
2744 "OEMDiagnosticDataType", "CollectDiagnosticData");
2745 return;
2746 }
2747 });
2748}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002749
Andrew Geisslercb92c032018-08-17 07:56:14 -07002750/**
2751 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2752 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002753inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07002754{
Andrew Geisslercb92c032018-08-17 07:56:14 -07002755 /**
2756 * Function handles POST method request.
2757 * The Clear Log actions does not require any parameter.The action deletes
2758 * all entries found in the Entries collection for this Log Service.
2759 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07002760
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002761 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
2762 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002763 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002764 .methods(boost::beast::http::verb::post)(
2765 [](const crow::Request&,
2766 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2767 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07002768
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002769 // Process response from Logging service.
2770 auto respHandler = [asyncResp](
2771 const boost::system::error_code ec) {
2772 BMCWEB_LOG_DEBUG
2773 << "doClearLog resp_handler callback: Done";
2774 if (ec)
2775 {
2776 // TODO Handle for specific error code
2777 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
2778 << ec;
2779 asyncResp->res.result(
2780 boost::beast::http::status::internal_server_error);
2781 return;
2782 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07002783
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002784 asyncResp->res.result(
2785 boost::beast::http::status::no_content);
2786 };
2787
2788 // Make call to Logging service to request Clear Log
2789 crow::connections::systemBus->async_method_call(
2790 respHandler, "xyz.openbmc_project.Logging",
2791 "/xyz/openbmc_project/logging",
2792 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2793 });
2794}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002795
2796/****************************************************
2797 * Redfish PostCode interfaces
2798 * using DBUS interface: getPostCodesTS
2799 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002800inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002801{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002802 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07002803 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002804 .methods(boost::beast::http::verb::get)(
2805 [](const crow::Request&,
2806 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2807 asyncResp->res.jsonValue = {
2808 {"@odata.id",
2809 "/redfish/v1/Systems/system/LogServices/PostCodes"},
2810 {"@odata.type", "#LogService.v1_1_0.LogService"},
2811 {"Name", "POST Code Log Service"},
2812 {"Description", "POST Code Log Service"},
2813 {"Id", "BIOS POST Code Log"},
2814 {"OverWritePolicy", "WrapsWhenFull"},
2815 {"Entries",
2816 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2817 "PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05302818
2819 std::pair<std::string, std::string> redfishDateTimeOffset =
2820 crow::utility::getDateTimeOffsetNow();
2821 asyncResp->res.jsonValue["DateTime"] =
2822 redfishDateTimeOffset.first;
2823 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2824 redfishDateTimeOffset.second;
2825
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002826 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2827 {"target",
2828 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2829 "Actions/LogService.ClearLog"}};
2830 });
2831}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002832
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002833inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002834{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002835 BMCWEB_ROUTE(app,
2836 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2837 "LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002838 // The following privilege is incorrect; It should be ConfigureManager
2839 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002840 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002841 .methods(boost::beast::http::verb::post)(
2842 [](const crow::Request&,
2843 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2844 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08002845
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002846 // Make call to post-code service to request clear all
2847 crow::connections::systemBus->async_method_call(
2848 [asyncResp](const boost::system::error_code ec) {
2849 if (ec)
2850 {
2851 // TODO Handle for specific error code
2852 BMCWEB_LOG_ERROR
2853 << "doClearPostCodes resp_handler got error "
2854 << ec;
2855 asyncResp->res.result(boost::beast::http::status::
2856 internal_server_error);
2857 messages::internalError(asyncResp->res);
2858 return;
2859 }
2860 },
2861 "xyz.openbmc_project.State.Boot.PostCode0",
2862 "/xyz/openbmc_project/State/Boot/PostCode0",
2863 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2864 });
2865}
ZhikuiRena3316fc2020-01-29 14:58:08 -08002866
2867static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08002868 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302869 const boost::container::flat_map<
2870 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002871 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2872 const uint64_t skip = 0, const uint64_t top = 0)
2873{
2874 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002875 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05302876 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08002877
2878 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002879 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08002880
2881 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302882 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
2883 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002884 {
2885 currentCodeIndex++;
2886 std::string postcodeEntryID =
2887 "B" + std::to_string(bootIndex) + "-" +
2888 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2889
2890 uint64_t usecSinceEpoch = code.first;
2891 uint64_t usTimeOffset = 0;
2892
2893 if (1 == currentCodeIndex)
2894 { // already incremented
2895 firstCodeTimeUs = code.first;
2896 }
2897 else
2898 {
2899 usTimeOffset = code.first - firstCodeTimeUs;
2900 }
2901
2902 // skip if no specific codeIndex is specified and currentCodeIndex does
2903 // not fall between top and skip
2904 if ((codeIndex == 0) &&
2905 (currentCodeIndex <= skip || currentCodeIndex > top))
2906 {
2907 continue;
2908 }
2909
Gunnar Mills4e0453b2020-07-08 14:00:30 -05002910 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08002911 // currentIndex
2912 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2913 {
2914 // This is done for simplicity. 1st entry is needed to calculate
2915 // time offset. To improve efficiency, one can get to the entry
2916 // directly (possibly with flatmap's nth method)
2917 continue;
2918 }
2919
2920 // currentCodeIndex is within top and skip or equal to specified code
2921 // index
2922
2923 // Get the Created time from the timestamp
2924 std::string entryTimeStr;
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -05002925 entryTimeStr = crow::utility::getDateTime(
2926 static_cast<std::time_t>(usecSinceEpoch / 1000 / 1000));
ZhikuiRena3316fc2020-01-29 14:58:08 -08002927
2928 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2929 std::ostringstream hexCode;
2930 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302931 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08002932 std::ostringstream timeOffsetStr;
2933 // Set Fixed -Point Notation
2934 timeOffsetStr << std::fixed;
2935 // Set precision to 4 digits
2936 timeOffsetStr << std::setprecision(4);
2937 // Add double to stream
2938 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2939 std::vector<std::string> messageArgs = {
2940 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2941
2942 // Get MessageArgs template from message registry
2943 std::string msg;
2944 if (message != nullptr)
2945 {
2946 msg = message->message;
2947
2948 // fill in this post code value
2949 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002950 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002951 {
2952 std::string argStr = "%" + std::to_string(++i);
2953 size_t argPos = msg.find(argStr);
2954 if (argPos != std::string::npos)
2955 {
2956 msg.replace(argPos, argStr.length(), messageArg);
2957 }
2958 }
2959 }
2960
Tim Leed4342a92020-04-27 11:47:58 +08002961 // Get Severity template from message registry
2962 std::string severity;
2963 if (message != nullptr)
2964 {
2965 severity = message->severity;
2966 }
2967
ZhikuiRena3316fc2020-01-29 14:58:08 -08002968 // add to AsyncResp
2969 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002970 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu647b3cd2021-07-05 12:43:56 +08002971 bmcLogEntry = {{"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Gunnar Mills743e9a12020-10-26 12:44:53 -05002972 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2973 "PostCodes/Entries/" +
2974 postcodeEntryID},
2975 {"Name", "POST Code Log Entry"},
2976 {"Id", postcodeEntryID},
2977 {"Message", std::move(msg)},
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05302978 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
Gunnar Mills743e9a12020-10-26 12:44:53 -05002979 {"MessageArgs", std::move(messageArgs)},
2980 {"EntryType", "Event"},
2981 {"Severity", std::move(severity)},
2982 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08002983 if (!std::get<std::vector<uint8_t>>(code.second).empty())
2984 {
2985 bmcLogEntry["AdditionalDataURI"] =
2986 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
2987 postcodeEntryID + "/attachment";
2988 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08002989 }
2990}
2991
zhanghch058d1b46d2021-04-01 11:18:24 +08002992static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002993 const uint16_t bootIndex,
2994 const uint64_t codeIndex)
2995{
2996 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05302997 [aResp, bootIndex,
2998 codeIndex](const boost::system::error_code ec,
2999 const boost::container::flat_map<
3000 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3001 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003002 if (ec)
3003 {
3004 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3005 messages::internalError(aResp->res);
3006 return;
3007 }
3008
3009 // skip the empty postcode boots
3010 if (postcode.empty())
3011 {
3012 return;
3013 }
3014
3015 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3016
3017 aResp->res.jsonValue["Members@odata.count"] =
3018 aResp->res.jsonValue["Members"].size();
3019 },
Jonathan Doman15124762021-01-07 17:54:17 -08003020 "xyz.openbmc_project.State.Boot.PostCode0",
3021 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003022 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3023 bootIndex);
3024}
3025
zhanghch058d1b46d2021-04-01 11:18:24 +08003026static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003027 const uint16_t bootIndex,
3028 const uint16_t bootCount,
3029 const uint64_t entryCount, const uint64_t skip,
3030 const uint64_t top)
3031{
3032 crow::connections::systemBus->async_method_call(
3033 [aResp, bootIndex, bootCount, entryCount, skip,
3034 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303035 const boost::container::flat_map<
3036 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3037 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003038 if (ec)
3039 {
3040 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3041 messages::internalError(aResp->res);
3042 return;
3043 }
3044
3045 uint64_t endCount = entryCount;
3046 if (!postcode.empty())
3047 {
3048 endCount = entryCount + postcode.size();
3049
3050 if ((skip < endCount) && ((top + skip) > entryCount))
3051 {
3052 uint64_t thisBootSkip =
3053 std::max(skip, entryCount) - entryCount;
3054 uint64_t thisBootTop =
3055 std::min(top + skip, endCount) - entryCount;
3056
3057 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3058 thisBootSkip, thisBootTop);
3059 }
3060 aResp->res.jsonValue["Members@odata.count"] = endCount;
3061 }
3062
3063 // continue to previous bootIndex
3064 if (bootIndex < bootCount)
3065 {
3066 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3067 bootCount, endCount, skip, top);
3068 }
3069 else
3070 {
3071 aResp->res.jsonValue["Members@odata.nextLink"] =
3072 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3073 "Entries?$skip=" +
3074 std::to_string(skip + top);
3075 }
3076 },
Jonathan Doman15124762021-01-07 17:54:17 -08003077 "xyz.openbmc_project.State.Boot.PostCode0",
3078 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003079 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3080 bootIndex);
3081}
3082
zhanghch058d1b46d2021-04-01 11:18:24 +08003083static void
3084 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3085 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003086{
3087 uint64_t entryCount = 0;
3088 crow::connections::systemBus->async_method_call(
3089 [aResp, entryCount, skip,
3090 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003091 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003092 if (ec)
3093 {
3094 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3095 messages::internalError(aResp->res);
3096 return;
3097 }
3098 auto pVal = std::get_if<uint16_t>(&bootCount);
3099 if (pVal)
3100 {
3101 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
3102 }
3103 else
3104 {
3105 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
3106 }
3107 },
Jonathan Doman15124762021-01-07 17:54:17 -08003108 "xyz.openbmc_project.State.Boot.PostCode0",
3109 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003110 "org.freedesktop.DBus.Properties", "Get",
3111 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
3112}
3113
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003114inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003115{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003116 BMCWEB_ROUTE(app,
3117 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003118 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003119 .methods(boost::beast::http::verb::get)(
3120 [](const crow::Request& req,
3121 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3122 asyncResp->res.jsonValue["@odata.type"] =
3123 "#LogEntryCollection.LogEntryCollection";
3124 asyncResp->res.jsonValue["@odata.id"] =
3125 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3126 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3127 asyncResp->res.jsonValue["Description"] =
3128 "Collection of POST Code Log Entries";
3129 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3130 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003131
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003132 uint64_t skip = 0;
3133 uint64_t top = maxEntriesPerPage; // Show max entries by default
3134 if (!getSkipParam(asyncResp, req, skip))
3135 {
3136 return;
3137 }
3138 if (!getTopParam(asyncResp, req, top))
3139 {
3140 return;
3141 }
3142 getCurrentBootNumber(asyncResp, skip, top);
3143 });
3144}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003145
George Liu647b3cd2021-07-05 12:43:56 +08003146/**
3147 * @brief Parse post code ID and get the current value and index value
3148 * eg: postCodeID=B1-2, currentValue=1, index=2
3149 *
3150 * @param[in] postCodeID Post Code ID
3151 * @param[out] currentValue Current value
3152 * @param[out] index Index value
3153 *
3154 * @return bool true if the parsing is successful, false the parsing fails
3155 */
3156inline static bool parsePostCode(const std::string& postCodeID,
3157 uint64_t& currentValue, uint16_t& index)
3158{
3159 std::vector<std::string> split;
3160 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3161 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3162 {
3163 return false;
3164 }
3165
3166 const char* start = split[0].data() + 1;
3167 const char* end = split[0].data() + split[0].size();
3168 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3169
3170 if (ptrIndex != end || ecIndex != std::errc())
3171 {
3172 return false;
3173 }
3174
3175 start = split[1].data();
3176 end = split[1].data() + split[1].size();
3177 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3178 if (ptrValue != end || ecValue != std::errc())
3179 {
3180 return false;
3181 }
3182
3183 return true;
3184}
3185
3186inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3187{
3188 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/"
3189 "Entries/<str>/attachment/")
3190 .privileges(redfish::privileges::getLogEntry)
3191 .methods(boost::beast::http::verb::get)(
3192 [](const crow::Request& req,
3193 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3194 const std::string& postCodeID) {
3195 if (!http_helpers::isOctetAccepted(
3196 req.getHeaderValue("Accept")))
3197 {
3198 asyncResp->res.result(
3199 boost::beast::http::status::bad_request);
3200 return;
3201 }
3202
3203 uint64_t currentValue = 0;
3204 uint16_t index = 0;
3205 if (!parsePostCode(postCodeID, currentValue, index))
3206 {
3207 messages::resourceNotFound(asyncResp->res, "LogEntry",
3208 postCodeID);
3209 return;
3210 }
3211
3212 crow::connections::systemBus->async_method_call(
3213 [asyncResp, postCodeID, currentValue](
3214 const boost::system::error_code ec,
3215 const std::vector<std::tuple<
3216 uint64_t, std::vector<uint8_t>>>& postcodes) {
3217 if (ec.value() == EBADR)
3218 {
3219 messages::resourceNotFound(asyncResp->res,
3220 "LogEntry", postCodeID);
3221 return;
3222 }
3223 if (ec)
3224 {
3225 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3226 messages::internalError(asyncResp->res);
3227 return;
3228 }
3229
3230 size_t value = static_cast<size_t>(currentValue) - 1;
3231 if (value == std::string::npos ||
3232 postcodes.size() < currentValue)
3233 {
3234 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3235 messages::resourceNotFound(asyncResp->res,
3236 "LogEntry", postCodeID);
3237 return;
3238 }
3239
3240 auto& [tID, code] = postcodes[value];
3241 if (code.empty())
3242 {
3243 BMCWEB_LOG_INFO << "No found post code data";
3244 messages::resourceNotFound(asyncResp->res,
3245 "LogEntry", postCodeID);
3246 return;
3247 }
3248
3249 std::string_view strData(
3250 reinterpret_cast<const char*>(code.data()),
3251 code.size());
3252
3253 asyncResp->res.addHeader("Content-Type",
3254 "application/octet-stream");
3255 asyncResp->res.addHeader("Content-Transfer-Encoding",
3256 "Base64");
3257 asyncResp->res.body() =
3258 crow::utility::base64encode(strData);
3259 },
3260 "xyz.openbmc_project.State.Boot.PostCode0",
3261 "/xyz/openbmc_project/State/Boot/PostCode0",
3262 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3263 index);
3264 });
3265}
3266
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003267inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003268{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003269 BMCWEB_ROUTE(
3270 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003271 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003272 .methods(boost::beast::http::verb::get)(
3273 [](const crow::Request&,
3274 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3275 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003276 uint16_t bootIndex = 0;
3277 uint64_t codeIndex = 0;
3278 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003279 {
3280 // Requested ID was not found
3281 messages::resourceMissingAtURI(asyncResp->res, targetID);
3282 return;
3283 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003284 if (bootIndex == 0 || codeIndex == 0)
3285 {
3286 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3287 << targetID;
3288 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003289
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003290 asyncResp->res.jsonValue["@odata.type"] =
3291 "#LogEntry.v1_4_0.LogEntry";
3292 asyncResp->res.jsonValue["@odata.id"] =
3293 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3294 "Entries";
3295 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3296 asyncResp->res.jsonValue["Description"] =
3297 "Collection of POST Code Log Entries";
3298 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3299 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003300
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003301 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3302 });
3303}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003304
Ed Tanous1da66f72018-07-27 16:13:37 -07003305} // namespace redfish