blob: 43be44b4a72e081d8e010de8c851395cdd2e3174 [file] [log] [blame]
Ed Tanous1da66f72018-07-27 16:13:37 -07001/*
2// Copyright (c) 2018 Intel Corporation
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15*/
16#pragma once
17
Spencer Kub7028eb2021-10-26 15:27:35 +080018#include "gzfile.hpp"
George Liu647b3cd2021-07-05 12:43:56 +080019#include "http_utility.hpp"
Spencer Kub7028eb2021-10-26 15:27:35 +080020#include "human_sort.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070021#include "registries.hpp"
22#include "registries/base_message_registry.hpp"
23#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080024#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070025
Jason M. Billse1f26342018-07-18 12:12:00 -070026#include <systemd/sd-journal.h>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060027#include <unistd.h>
Jason M. Billse1f26342018-07-18 12:12:00 -070028
John Edward Broadbent7e860f12021-04-08 15:57:16 -070029#include <app.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060030#include <boost/algorithm/string/replace.hpp>
Jason M. Bills4851d452019-03-28 11:27:48 -070031#include <boost/algorithm/string/split.hpp>
Adriana Kobylak400fd1f2021-01-29 09:01:30 -060032#include <boost/beast/http.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070033#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080034#include <boost/system/linux_error.hpp>
Ed Tanous168e20c2021-12-13 14:39:53 -080035#include <dbus_utility.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070036#include <error_messages.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070037#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050038
George Liu647b3cd2021-07-05 12:43:56 +080039#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070040#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080041#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070042#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070043#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080044#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070045
46namespace redfish
47{
48
Gunnar Mills1214b7e2020-06-04 10:11:30 -050049constexpr char const* crashdumpObject = "com.intel.crashdump";
50constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050051constexpr char const* crashdumpInterface = "com.intel.crashdump";
52constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070053 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050054constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070055 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070056constexpr char const* crashdumpTelemetryInterface =
57 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070058
Jason M. Bills4851d452019-03-28 11:27:48 -070059namespace message_registries
60{
Ed Tanous26702d02021-11-03 15:02:33 -070061static const Message*
62 getMessageFromRegistry(const std::string& messageKey,
63 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070064{
Ed Tanous26702d02021-11-03 15:02:33 -070065 std::span<const MessageEntry>::iterator messageIt = std::find_if(
66 registry.begin(), registry.end(),
67 [&messageKey](const MessageEntry& messageEntry) {
68 return !std::strcmp(messageEntry.first, messageKey.c_str());
69 });
70 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070071 {
72 return &messageIt->second;
73 }
74
75 return nullptr;
76}
77
Gunnar Mills1214b7e2020-06-04 10:11:30 -050078static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070079{
80 // Redfish MessageIds are in the form
81 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
82 // the right Message
83 std::vector<std::string> fields;
84 fields.reserve(4);
85 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050086 std::string& registryName = fields[0];
87 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070088
89 // Find the right registry and check it for the MessageKey
90 if (std::string(base::header.registryPrefix) == registryName)
91 {
92 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070093 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070094 }
95 if (std::string(openbmc::header.registryPrefix) == registryName)
96 {
97 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070098 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070099 }
100 return nullptr;
101}
102} // namespace message_registries
103
James Feistf6150402019-01-08 10:36:20 -0800104namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700105
Ed Tanous168e20c2021-12-13 14:39:53 -0800106using GetManagedPropertyType =
107 boost::container::flat_map<std::string, dbus::utility::DbusVariantType>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700108
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700110{
Ed Tanousd4d25792020-09-29 15:15:03 -0700111 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
112 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
113 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
114 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700115 {
116 return "Critical";
117 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700118 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
119 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
120 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700121 {
122 return "OK";
123 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700124 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700125 {
126 return "Warning";
127 }
128 return "";
129}
130
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700131inline static int getJournalMetadata(sd_journal* journal,
132 const std::string_view& field,
133 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700134{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500135 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700136 size_t length = 0;
137 int ret = 0;
138 // Get the metadata from the requested field of the journal entry
Ed Tanous46ff87b2022-01-07 09:25:51 -0800139 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
140 const void** dataVoid = reinterpret_cast<const void**>(&data);
141
142 ret = sd_journal_get_data(journal, field.data(), dataVoid, &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700143 if (ret < 0)
144 {
145 return ret;
146 }
Ed Tanous39e77502019-03-04 17:35:53 -0800147 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700148 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000149 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700150 return ret;
151}
152
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700153inline static int getJournalMetadata(sd_journal* journal,
154 const std::string_view& field,
155 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700156{
157 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800158 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700159 // Get the metadata from the requested field of the journal entry
160 ret = getJournalMetadata(journal, field, metadata);
161 if (ret < 0)
162 {
163 return ret;
164 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000165 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700166 return ret;
167}
168
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700169inline static bool getEntryTimestamp(sd_journal* journal,
170 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800171{
172 int ret = 0;
173 uint64_t timestamp = 0;
174 ret = sd_journal_get_realtime_usec(journal, &timestamp);
175 if (ret < 0)
176 {
177 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
178 << strerror(-ret);
179 return false;
180 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800181 entryTimestamp = crow::utility::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500182 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800183}
Ed Tanous67df0732021-10-26 11:23:56 -0700184#ifdef NEW_BOOST_URL
185static bool getSkipParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
186 const crow::Request& req, uint64_t& skip)
187{
188 boost::urls::params_view::iterator it = req.urlView.params().find("$skip");
189 if (it != req.urlView.params().end())
190 {
191 std::from_chars_result r = std::from_chars(
192 (*it).value.data(), (*it).value.data() + (*it).value.size(), skip);
193 if (r.ec != std::errc())
194 {
195 messages::queryParameterValueTypeError(asyncResp->res, "", "$skip");
196 return false;
197 }
198 }
199 return true;
200}
201
202static constexpr const uint64_t maxEntriesPerPage = 1000;
203static bool getTopParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
204 const crow::Request& req, uint64_t& top)
205{
206 boost::urls::params_view::iterator it = req.urlView.params().find("$top");
207 if (it != req.urlView.params().end())
208 {
209 std::from_chars_result r = std::from_chars(
210 (*it).value.data(), (*it).value.data() + (*it).value.size(), top);
211 if (r.ec != std::errc())
212 {
213 messages::queryParameterValueTypeError(asyncResp->res, "", "$top");
214 return false;
215 }
216 if (top < 1U || top > maxEntriesPerPage)
217 {
218
219 messages::queryParameterOutOfRange(
220 asyncResp->res, std::to_string(top), "$top",
221 "1-" + std::to_string(maxEntriesPerPage));
222 return false;
223 }
224 }
225 return true;
226}
227
228#else
ZhikuiRena3316fc2020-01-29 14:58:08 -0800229
zhanghch058d1b46d2021-04-01 11:18:24 +0800230static bool getSkipParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
231 const crow::Request& req, uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700232{
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700233 boost::urls::query_params_view::iterator it = req.urlParams.find("$skip");
James Feist5a7e8772020-07-22 09:08:38 -0700234 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700235 {
James Feist5a7e8772020-07-22 09:08:38 -0700236 std::string skipParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500237 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700238 skip = std::strtoul(skipParam.c_str(), &ptr, 10);
239 if (skipParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700240 {
241
zhanghch058d1b46d2021-04-01 11:18:24 +0800242 messages::queryParameterValueTypeError(
243 asyncResp->res, std::string(skipParam), "$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700244 return false;
245 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700246 }
247 return true;
248}
249
Ed Tanous271584a2019-07-09 16:24:22 -0700250static constexpr const uint64_t maxEntriesPerPage = 1000;
zhanghch058d1b46d2021-04-01 11:18:24 +0800251static bool getTopParam(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
252 const crow::Request& req, uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700253{
Ed Tanousd32c4fa2021-09-14 13:16:51 -0700254 boost::urls::query_params_view::iterator it = req.urlParams.find("$top");
James Feist5a7e8772020-07-22 09:08:38 -0700255 if (it != req.urlParams.end())
Jason M. Bills16428a12018-11-02 12:42:29 -0700256 {
James Feist5a7e8772020-07-22 09:08:38 -0700257 std::string topParam = it->value();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500258 char* ptr = nullptr;
James Feist5a7e8772020-07-22 09:08:38 -0700259 top = std::strtoul(topParam.c_str(), &ptr, 10);
260 if (topParam.empty() || *ptr != '\0')
Jason M. Bills16428a12018-11-02 12:42:29 -0700261 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800262 messages::queryParameterValueTypeError(
263 asyncResp->res, std::string(topParam), "$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700264 return false;
265 }
Ed Tanous271584a2019-07-09 16:24:22 -0700266 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700267 {
268
269 messages::queryParameterOutOfRange(
zhanghch058d1b46d2021-04-01 11:18:24 +0800270 asyncResp->res, std::to_string(top), "$top",
Jason M. Bills16428a12018-11-02 12:42:29 -0700271 "1-" + std::to_string(maxEntriesPerPage));
272 return false;
273 }
274 }
275 return true;
276}
277
Ed Tanous67df0732021-10-26 11:23:56 -0700278#endif
279
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700280inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
281 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700282{
283 int ret = 0;
284 static uint64_t prevTs = 0;
285 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700286 if (firstEntry)
287 {
288 prevTs = 0;
289 }
290
Jason M. Bills16428a12018-11-02 12:42:29 -0700291 // Get the entry timestamp
292 uint64_t curTs = 0;
293 ret = sd_journal_get_realtime_usec(journal, &curTs);
294 if (ret < 0)
295 {
296 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
297 << strerror(-ret);
298 return false;
299 }
300 // If the timestamp isn't unique, increment the index
301 if (curTs == prevTs)
302 {
303 index++;
304 }
305 else
306 {
307 // Otherwise, reset it
308 index = 0;
309 }
310 // Save the timestamp
311 prevTs = curTs;
312
313 entryID = std::to_string(curTs);
314 if (index > 0)
315 {
316 entryID += "_" + std::to_string(index);
317 }
318 return true;
319}
320
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500321static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700322 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700323{
Ed Tanous271584a2019-07-09 16:24:22 -0700324 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700325 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700326 if (firstEntry)
327 {
328 prevTs = 0;
329 }
330
Jason M. Bills95820182019-04-22 16:25:34 -0700331 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700332 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700333 std::tm timeStruct = {};
334 std::istringstream entryStream(logEntry);
335 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
336 {
337 curTs = std::mktime(&timeStruct);
338 }
339 // If the timestamp isn't unique, increment the index
340 if (curTs == prevTs)
341 {
342 index++;
343 }
344 else
345 {
346 // Otherwise, reset it
347 index = 0;
348 }
349 // Save the timestamp
350 prevTs = curTs;
351
352 entryID = std::to_string(curTs);
353 if (index > 0)
354 {
355 entryID += "_" + std::to_string(index);
356 }
357 return true;
358}
359
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700360inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800361 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
362 const std::string& entryID, uint64_t& timestamp,
363 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700364{
365 if (entryID.empty())
366 {
367 return false;
368 }
369 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800370 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700371
Ed Tanous81ce6092020-12-17 16:54:55 +0000372 auto underscorePos = tsStr.find('_');
Jason M. Bills16428a12018-11-02 12:42:29 -0700373 if (underscorePos != tsStr.npos)
374 {
375 // Timestamp has an index
376 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800377 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700378 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700379 auto [ptr, ec] = std::from_chars(
380 indexStr.data(), indexStr.data() + indexStr.size(), index);
381 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700382 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800383 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700384 return false;
385 }
386 }
387 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700388 auto [ptr, ec] =
389 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
390 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700391 {
zhanghch058d1b46d2021-04-01 11:18:24 +0800392 messages::resourceMissingAtURI(asyncResp->res, entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700393 return false;
394 }
395 return true;
396}
397
Jason M. Bills95820182019-04-22 16:25:34 -0700398static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500399 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700400{
401 static const std::filesystem::path redfishLogDir = "/var/log";
402 static const std::string redfishLogFilename = "redfish";
403
404 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500405 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700406 std::filesystem::directory_iterator(redfishLogDir))
407 {
408 // If we find a redfish log file, save the path
409 std::string filename = dirEnt.path().filename();
410 if (boost::starts_with(filename, redfishLogFilename))
411 {
412 redfishLogFiles.emplace_back(redfishLogDir / filename);
413 }
414 }
415 // As the log files rotate, they are appended with a ".#" that is higher for
416 // the older logs. Since we don't expect more than 10 log files, we
417 // can just sort the list to get them in order from newest to oldest
418 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
419
420 return !redfishLogFiles.empty();
421}
422
zhanghch058d1b46d2021-04-01 11:18:24 +0800423inline void
424 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
425 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500426{
427 std::string dumpPath;
428 if (dumpType == "BMC")
429 {
430 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
431 }
432 else if (dumpType == "System")
433 {
434 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
435 }
436 else
437 {
438 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
439 messages::internalError(asyncResp->res);
440 return;
441 }
442
443 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800444 [asyncResp, dumpPath,
445 dumpType](const boost::system::error_code ec,
446 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500447 if (ec)
448 {
449 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
450 messages::internalError(asyncResp->res);
451 return;
452 }
453
454 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
455 entriesArray = nlohmann::json::array();
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500456 std::string dumpEntryPath =
457 "/xyz/openbmc_project/dump/" +
458 std::string(boost::algorithm::to_lower_copy(dumpType)) +
459 "/entry/";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500460
461 for (auto& object : resp)
462 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500463 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500464 {
465 continue;
466 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800467 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500468 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500469 std::string dumpStatus;
470 nlohmann::json thisEntry;
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000471
472 std::string entryID = object.first.filename();
473 if (entryID.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500474 {
475 continue;
476 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500477
478 for (auto& interfaceMap : object.second)
479 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500480 if (interfaceMap.first ==
481 "xyz.openbmc_project.Common.Progress")
482 {
483 for (auto& propertyMap : interfaceMap.second)
484 {
485 if (propertyMap.first == "Status")
486 {
487 auto status = std::get_if<std::string>(
488 &propertyMap.second);
489 if (status == nullptr)
490 {
491 messages::internalError(asyncResp->res);
492 break;
493 }
494 dumpStatus = *status;
495 }
496 }
497 }
498 else if (interfaceMap.first ==
499 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500500 {
501
502 for (auto& propertyMap : interfaceMap.second)
503 {
504 if (propertyMap.first == "Size")
505 {
506 auto sizePtr =
507 std::get_if<uint64_t>(&propertyMap.second);
508 if (sizePtr == nullptr)
509 {
510 messages::internalError(asyncResp->res);
511 break;
512 }
513 size = *sizePtr;
514 break;
515 }
516 }
517 }
518 else if (interfaceMap.first ==
519 "xyz.openbmc_project.Time.EpochTime")
520 {
521
522 for (auto& propertyMap : interfaceMap.second)
523 {
524 if (propertyMap.first == "Elapsed")
525 {
526 const uint64_t* usecsTimeStamp =
527 std::get_if<uint64_t>(&propertyMap.second);
528 if (usecsTimeStamp == nullptr)
529 {
530 messages::internalError(asyncResp->res);
531 break;
532 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800533 timestamp = (*usecsTimeStamp / 1000 / 1000);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500534 break;
535 }
536 }
537 }
538 }
539
George Liu0fda0f12021-11-16 10:06:17 +0800540 if (dumpStatus !=
541 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500542 !dumpStatus.empty())
543 {
544 // Dump status is not Complete, no need to enumerate
545 continue;
546 }
547
George Liu647b3cd2021-07-05 12:43:56 +0800548 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500549 thisEntry["@odata.id"] = dumpPath + entryID;
550 thisEntry["Id"] = entryID;
551 thisEntry["EntryType"] = "Event";
Nan Zhou1d8782e2021-11-29 22:23:18 -0800552 thisEntry["Created"] =
553 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500554 thisEntry["Name"] = dumpType + " Dump Entry";
555
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500556 thisEntry["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500557
558 if (dumpType == "BMC")
559 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500560 thisEntry["DiagnosticDataType"] = "Manager";
561 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500562 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
563 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500564 }
565 else if (dumpType == "System")
566 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500567 thisEntry["DiagnosticDataType"] = "OEM";
568 thisEntry["OEMDiagnosticDataType"] = "System";
569 thisEntry["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500570 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
571 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500572 }
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500573 entriesArray.push_back(std::move(thisEntry));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500574 }
575 asyncResp->res.jsonValue["Members@odata.count"] =
576 entriesArray.size();
577 },
578 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
579 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
580}
581
zhanghch058d1b46d2021-04-01 11:18:24 +0800582inline void
583 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
584 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500585{
586 std::string dumpPath;
587 if (dumpType == "BMC")
588 {
589 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
590 }
591 else if (dumpType == "System")
592 {
593 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
594 }
595 else
596 {
597 BMCWEB_LOG_ERROR << "Invalid dump type" << dumpType;
598 messages::internalError(asyncResp->res);
599 return;
600 }
601
602 crow::connections::systemBus->async_method_call(
Ed Tanous711ac7a2021-12-20 09:34:41 -0800603 [asyncResp, entryID, dumpPath,
604 dumpType](const boost::system::error_code ec,
605 dbus::utility::ManagedObjectType& resp) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500606 if (ec)
607 {
608 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
609 messages::internalError(asyncResp->res);
610 return;
611 }
612
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500613 bool foundDumpEntry = false;
614 std::string dumpEntryPath =
615 "/xyz/openbmc_project/dump/" +
616 std::string(boost::algorithm::to_lower_copy(dumpType)) +
617 "/entry/";
618
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500619 for (auto& objectPath : resp)
620 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500621 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500622 {
623 continue;
624 }
625
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500626 foundDumpEntry = true;
Nan Zhou1d8782e2021-11-29 22:23:18 -0800627 uint64_t timestamp = 0;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500628 uint64_t size = 0;
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500629 std::string dumpStatus;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500630
631 for (auto& interfaceMap : objectPath.second)
632 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500633 if (interfaceMap.first ==
634 "xyz.openbmc_project.Common.Progress")
635 {
636 for (auto& propertyMap : interfaceMap.second)
637 {
638 if (propertyMap.first == "Status")
639 {
640 auto status = std::get_if<std::string>(
641 &propertyMap.second);
642 if (status == nullptr)
643 {
644 messages::internalError(asyncResp->res);
645 break;
646 }
647 dumpStatus = *status;
648 }
649 }
650 }
651 else if (interfaceMap.first ==
652 "xyz.openbmc_project.Dump.Entry")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500653 {
654 for (auto& propertyMap : interfaceMap.second)
655 {
656 if (propertyMap.first == "Size")
657 {
658 auto sizePtr =
659 std::get_if<uint64_t>(&propertyMap.second);
660 if (sizePtr == nullptr)
661 {
662 messages::internalError(asyncResp->res);
663 break;
664 }
665 size = *sizePtr;
666 break;
667 }
668 }
669 }
670 else if (interfaceMap.first ==
671 "xyz.openbmc_project.Time.EpochTime")
672 {
673 for (auto& propertyMap : interfaceMap.second)
674 {
675 if (propertyMap.first == "Elapsed")
676 {
677 const uint64_t* usecsTimeStamp =
678 std::get_if<uint64_t>(&propertyMap.second);
679 if (usecsTimeStamp == nullptr)
680 {
681 messages::internalError(asyncResp->res);
682 break;
683 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800684 timestamp = *usecsTimeStamp / 1000 / 1000;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500685 break;
686 }
687 }
688 }
689 }
690
George Liu0fda0f12021-11-16 10:06:17 +0800691 if (dumpStatus !=
692 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500693 !dumpStatus.empty())
694 {
695 // Dump status is not Complete
696 // return not found until status is changed to Completed
697 messages::resourceNotFound(asyncResp->res,
698 dumpType + " dump", entryID);
699 return;
700 }
701
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500702 asyncResp->res.jsonValue["@odata.type"] =
George Liu647b3cd2021-07-05 12:43:56 +0800703 "#LogEntry.v1_8_0.LogEntry";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500704 asyncResp->res.jsonValue["@odata.id"] = dumpPath + entryID;
705 asyncResp->res.jsonValue["Id"] = entryID;
706 asyncResp->res.jsonValue["EntryType"] = "Event";
707 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -0800708 crow::utility::getDateTimeUint(timestamp);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500709 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
710
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500711 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500712
713 if (dumpType == "BMC")
714 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500715 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
716 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500717 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/" +
718 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500719 }
720 else if (dumpType == "System")
721 {
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500722 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
723 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500724 "System";
Asmitha Karunanithid337bb72020-09-21 10:34:02 -0500725 asyncResp->res.jsonValue["AdditionalDataURI"] =
Abhishek Patelde8d94a2021-05-13 22:57:36 -0500726 "/redfish/v1/Systems/system/LogServices/Dump/Entries/" +
727 entryID + "/attachment";
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500728 }
729 }
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500730 if (foundDumpEntry == false)
731 {
732 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
733 messages::internalError(asyncResp->res);
734 return;
735 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500736 },
737 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
738 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
739}
740
zhanghch058d1b46d2021-04-01 11:18:24 +0800741inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800742 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500743 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500744{
George Liu3de8d8b2021-03-22 17:49:39 +0800745 auto respHandler = [asyncResp,
746 entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500747 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
748 if (ec)
749 {
George Liu3de8d8b2021-03-22 17:49:39 +0800750 if (ec.value() == EBADR)
751 {
752 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
753 return;
754 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500755 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
756 << ec;
757 messages::internalError(asyncResp->res);
758 return;
759 }
760 };
761 crow::connections::systemBus->async_method_call(
762 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500763 "/xyz/openbmc_project/dump/" +
764 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
765 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500766 "xyz.openbmc_project.Object.Delete", "Delete");
767}
768
zhanghch058d1b46d2021-04-01 11:18:24 +0800769inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700770 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800771 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
772 const uint32_t& dumpId, const std::string& dumpPath,
773 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500774{
775 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500776 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500777 boost::system::error_code err, sdbusplus::message::message& m,
778 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanouscb13a392020-07-25 19:02:03 +0000779 if (err)
780 {
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500781 BMCWEB_LOG_ERROR << "Error in creating a dump";
782 taskData->state = "Cancelled";
783 return task::completed;
Ed Tanouscb13a392020-07-25 19:02:03 +0000784 }
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500785 std::vector<std::pair<
Ed Tanous168e20c2021-12-13 14:39:53 -0800786 std::string, std::vector<std::pair<
787 std::string, dbus::utility::DbusVariantType>>>>
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500788 interfacesList;
789
790 sdbusplus::message::object_path objPath;
791
792 m.read(objPath, interfacesList);
793
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500794 if (objPath.str ==
795 "/xyz/openbmc_project/dump/" +
796 std::string(boost::algorithm::to_lower_copy(dumpType)) +
797 "/entry/" + std::to_string(dumpId))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500798 {
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500799 nlohmann::json retMessage = messages::success();
800 taskData->messages.emplace_back(retMessage);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500801
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500802 std::string headerLoc =
803 "Location: " + dumpPath + std::to_string(dumpId);
804 taskData->payload->httpHeaders.emplace_back(
805 std::move(headerLoc));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500806
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500807 taskData->state = "Completed";
808 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500809 }
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500810 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500811 },
812 "type='signal',interface='org.freedesktop.DBus."
813 "ObjectManager',"
814 "member='InterfacesAdded', "
815 "path='/xyz/openbmc_project/dump'");
816
817 task->startTimer(std::chrono::minutes(3));
818 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700819 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500820}
821
zhanghch058d1b46d2021-04-01 11:18:24 +0800822inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
823 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500824{
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500825
826 std::string dumpPath;
827 if (dumpType == "BMC")
828 {
829 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
830 }
831 else if (dumpType == "System")
832 {
833 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
834 }
835 else
836 {
837 BMCWEB_LOG_ERROR << "Invalid dump type: " << dumpType;
838 messages::internalError(asyncResp->res);
839 return;
840 }
841
842 std::optional<std::string> diagnosticDataType;
843 std::optional<std::string> oemDiagnosticDataType;
844
845 if (!redfish::json_util::readJson(
846 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
847 "OEMDiagnosticDataType", oemDiagnosticDataType))
848 {
849 return;
850 }
851
852 if (dumpType == "System")
853 {
854 if (!oemDiagnosticDataType || !diagnosticDataType)
855 {
856 BMCWEB_LOG_ERROR << "CreateDump action parameter "
857 "'DiagnosticDataType'/"
858 "'OEMDiagnosticDataType' value not found!";
859 messages::actionParameterMissing(
860 asyncResp->res, "CollectDiagnosticData",
861 "DiagnosticDataType & OEMDiagnosticDataType");
862 return;
863 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700864 if ((*oemDiagnosticDataType != "System") ||
865 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500866 {
867 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
868 messages::invalidObject(asyncResp->res,
869 "System Dump creation parameters");
870 return;
871 }
872 }
873 else if (dumpType == "BMC")
874 {
875 if (!diagnosticDataType)
876 {
George Liu0fda0f12021-11-16 10:06:17 +0800877 BMCWEB_LOG_ERROR
878 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500879 messages::actionParameterMissing(
880 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
881 return;
882 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700883 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500884 {
885 BMCWEB_LOG_ERROR
886 << "Wrong parameter value passed for 'DiagnosticDataType'";
887 messages::invalidObject(asyncResp->res,
888 "BMC Dump creation parameters");
889 return;
890 }
891 }
892
893 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700894 [asyncResp, payload(task::Payload(req)), dumpPath,
895 dumpType](const boost::system::error_code ec,
896 const uint32_t& dumpId) mutable {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500897 if (ec)
898 {
899 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
900 messages::internalError(asyncResp->res);
901 return;
902 }
903 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
904
Ed Tanous98be3e32021-09-16 15:05:36 -0700905 createDumpTaskCallback(std::move(payload), asyncResp, dumpId,
906 dumpPath, dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500907 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500908 "xyz.openbmc_project.Dump.Manager",
909 "/xyz/openbmc_project/dump/" +
910 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500911 "xyz.openbmc_project.Dump.Create", "CreateDump");
912}
913
zhanghch058d1b46d2021-04-01 11:18:24 +0800914inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
915 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500916{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500917 std::string dumpTypeLowerCopy =
918 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800919
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500920 crow::connections::systemBus->async_method_call(
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500921 [asyncResp, dumpType](const boost::system::error_code ec,
922 const std::vector<std::string>& subTreePaths) {
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500923 if (ec)
924 {
925 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
926 messages::internalError(asyncResp->res);
927 return;
928 }
929
930 for (const std::string& path : subTreePaths)
931 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000932 sdbusplus::message::object_path objPath(path);
933 std::string logID = objPath.filename();
934 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500935 {
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000936 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500937 }
Ed Tanous2dfd18e2020-12-18 00:41:31 +0000938 deleteDumpEntry(asyncResp, logID, dumpType);
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500939 }
940 },
941 "xyz.openbmc_project.ObjectMapper",
942 "/xyz/openbmc_project/object_mapper",
943 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500944 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
945 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
946 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500947}
948
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700949inline static void parseCrashdumpParameters(
Ed Tanous168e20c2021-12-13 14:39:53 -0800950 const std::vector<std::pair<std::string, dbus::utility::DbusVariantType>>&
951 params,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500952 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700953{
954 for (auto property : params)
955 {
956 if (property.first == "Timestamp")
957 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500958 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500959 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700960 if (value != nullptr)
961 {
962 timestamp = *value;
963 }
964 }
965 else if (property.first == "Filename")
966 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500967 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500968 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700969 if (value != nullptr)
970 {
971 filename = *value;
972 }
973 }
974 else if (property.first == "Log")
975 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500976 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500977 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700978 if (value != nullptr)
979 {
980 logfile = *value;
981 }
982 }
983 }
984}
985
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500986constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700987inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700988{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800989 /**
990 * Functions triggers appropriate requests on DBus
991 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700992 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700993 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700994 .methods(boost::beast::http::verb::get)(
995 [](const crow::Request&,
996 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
997
998 {
999 // Collections don't include the static data added by SubRoute
1000 // because it has a duplicate entry for members
1001 asyncResp->res.jsonValue["@odata.type"] =
1002 "#LogServiceCollection.LogServiceCollection";
1003 asyncResp->res.jsonValue["@odata.id"] =
1004 "/redfish/v1/Systems/system/LogServices";
1005 asyncResp->res.jsonValue["Name"] =
1006 "System Log Services Collection";
1007 asyncResp->res.jsonValue["Description"] =
1008 "Collection of LogServices for this Computer System";
1009 nlohmann::json& logServiceArray =
1010 asyncResp->res.jsonValue["Members"];
1011 logServiceArray = nlohmann::json::array();
1012 logServiceArray.push_back(
1013 {{"@odata.id",
1014 "/redfish/v1/Systems/system/LogServices/EventLog"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05001015#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001016 logServiceArray.push_back(
1017 {{"@odata.id",
1018 "/redfish/v1/Systems/system/LogServices/Dump"}});
raviteja-bc9bb6862020-02-03 11:53:32 -06001019#endif
1020
Jason M. Billsd53dd412019-02-12 17:16:22 -08001021#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001022 logServiceArray.push_back(
1023 {{"@odata.id",
1024 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -08001025#endif
Spencer Kub7028eb2021-10-26 15:27:35 +08001026
1027#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
1028 logServiceArray.push_back(
1029 {{"@odata.id",
1030 "/redfish/v1/Systems/system/LogServices/HostLogger"}});
1031#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001032 asyncResp->res.jsonValue["Members@odata.count"] =
1033 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08001034
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001035 crow::connections::systemBus->async_method_call(
1036 [asyncResp](const boost::system::error_code ec,
1037 const std::vector<std::string>& subtreePath) {
1038 if (ec)
1039 {
1040 BMCWEB_LOG_ERROR << ec;
1041 return;
1042 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08001043
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001044 for (auto& pathStr : subtreePath)
1045 {
1046 if (pathStr.find("PostCode") != std::string::npos)
1047 {
1048 nlohmann::json& logServiceArrayLocal =
1049 asyncResp->res.jsonValue["Members"];
1050 logServiceArrayLocal.push_back(
George Liu0fda0f12021-11-16 10:06:17 +08001051 {{"@odata.id",
1052 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001053 asyncResp->res
1054 .jsonValue["Members@odata.count"] =
1055 logServiceArrayLocal.size();
1056 return;
1057 }
1058 }
1059 },
1060 "xyz.openbmc_project.ObjectMapper",
1061 "/xyz/openbmc_project/object_mapper",
1062 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/",
1063 0, std::array<const char*, 1>{postCodeIface});
1064 });
1065}
1066
1067inline void requestRoutesEventLogService(App& app)
1068{
1069 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -07001070 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001071 .methods(
1072 boost::beast::http::verb::
1073 get)([](const crow::Request&,
1074 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1075 asyncResp->res.jsonValue["@odata.id"] =
1076 "/redfish/v1/Systems/system/LogServices/EventLog";
1077 asyncResp->res.jsonValue["@odata.type"] =
1078 "#LogService.v1_1_0.LogService";
1079 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1080 asyncResp->res.jsonValue["Description"] =
1081 "System Event Log Service";
1082 asyncResp->res.jsonValue["Id"] = "EventLog";
1083 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05301084
1085 std::pair<std::string, std::string> redfishDateTimeOffset =
1086 crow::utility::getDateTimeOffsetNow();
1087
1088 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1089 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1090 redfishDateTimeOffset.second;
1091
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001092 asyncResp->res.jsonValue["Entries"] = {
1093 {"@odata.id",
1094 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
1095 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1096
George Liu0fda0f12021-11-16 10:06:17 +08001097 {"target",
1098 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001099 });
1100}
1101
1102inline void requestRoutesJournalEventLogClear(App& app)
1103{
1104 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1105 "LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001106 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001107 .methods(boost::beast::http::verb::post)(
1108 [](const crow::Request&,
1109 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1110 // Clear the EventLog by deleting the log files
1111 std::vector<std::filesystem::path> redfishLogFiles;
1112 if (getRedfishLogFiles(redfishLogFiles))
ZhikuiRena3316fc2020-01-29 14:58:08 -08001113 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001114 for (const std::filesystem::path& file : redfishLogFiles)
ZhikuiRena3316fc2020-01-29 14:58:08 -08001115 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001116 std::error_code ec;
1117 std::filesystem::remove(file, ec);
ZhikuiRena3316fc2020-01-29 14:58:08 -08001118 }
1119 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001120
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001121 // Reload rsyslog so it knows to start new log files
1122 crow::connections::systemBus->async_method_call(
1123 [asyncResp](const boost::system::error_code ec) {
1124 if (ec)
1125 {
1126 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: "
1127 << ec;
1128 messages::internalError(asyncResp->res);
1129 return;
1130 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001131
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001132 messages::success(asyncResp->res);
1133 },
1134 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1135 "org.freedesktop.systemd1.Manager", "ReloadUnit",
1136 "rsyslog.service", "replace");
1137 });
1138}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001139
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001140static int fillEventLogEntryJson(const std::string& logEntryID,
Ed Tanousb5a76932020-09-29 16:16:58 -07001141 const std::string& logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001142 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001143{
Jason M. Bills95820182019-04-22 16:25:34 -07001144 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001145 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001146 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001147 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001148 {
1149 return 1;
1150 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001151 std::string timestamp = logEntry.substr(0, space);
1152 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001153 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001154 if (entryStart == std::string::npos)
1155 {
1156 return 1;
1157 }
1158 std::string_view entry(logEntry);
1159 entry.remove_prefix(entryStart);
1160 // Use split to separate the entry into its fields
1161 std::vector<std::string> logEntryFields;
1162 boost::split(logEntryFields, entry, boost::is_any_of(","),
1163 boost::token_compress_on);
1164 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001165 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001166 {
1167 return 1;
1168 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001169 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001170
Jason M. Bills4851d452019-03-28 11:27:48 -07001171 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001172 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -07001173 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001174
Jason M. Bills4851d452019-03-28 11:27:48 -07001175 std::string msg;
1176 std::string severity;
1177 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001178 {
Jason M. Bills4851d452019-03-28 11:27:48 -07001179 msg = message->message;
1180 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001181 }
1182
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001183 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001184 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001185 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001186 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001187 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001188 // If the first string is empty, assume there are no MessageArgs
1189 std::size_t messageArgsSize = 0;
1190 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001191 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001192 messageArgsSize = logEntryFields.size() - 1;
1193 }
1194
Ed Tanous23a21a12020-07-25 04:45:05 +00001195 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001196
1197 // Fill the MessageArgs into the Message
1198 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001199 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001200 {
1201 std::string argStr = "%" + std::to_string(++i);
1202 size_t argPos = msg.find(argStr);
1203 if (argPos != std::string::npos)
1204 {
1205 msg.replace(argPos, argStr.length(), messageArg);
1206 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001207 }
1208 }
1209
Jason M. Bills95820182019-04-22 16:25:34 -07001210 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1211 // format which matches the Redfish format except for the fractional seconds
1212 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001213 std::size_t dot = timestamp.find_first_of('.');
1214 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001215 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001216 {
Jason M. Bills95820182019-04-22 16:25:34 -07001217 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001218 }
1219
1220 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -07001221 logEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08001222 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -08001223 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -07001224 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -07001225 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001226 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -07001227 {"Id", logEntryID},
1228 {"Message", std::move(msg)},
1229 {"MessageId", std::move(messageID)},
Ed Tanousf23b7292020-10-15 09:41:17 -07001230 {"MessageArgs", messageArgs},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001231 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -07001232 {"Severity", std::move(severity)},
1233 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001234 return 0;
1235}
1236
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001237inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001238{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001239 BMCWEB_ROUTE(app,
1240 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001241 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001242 .methods(boost::beast::http::verb::get)(
1243 [](const crow::Request& req,
1244 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1245 uint64_t skip = 0;
1246 uint64_t top = maxEntriesPerPage; // Show max entries by default
1247 if (!getSkipParam(asyncResp, req, skip))
Jason M. Bills95820182019-04-22 16:25:34 -07001248 {
Jason M. Bills95820182019-04-22 16:25:34 -07001249 return;
1250 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001251 if (!getTopParam(asyncResp, req, top))
Jason M. Bills897967d2019-07-29 17:05:30 -07001252 {
Jason M. Bills897967d2019-07-29 17:05:30 -07001253 return;
1254 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001255 // Collections don't include the static data added by SubRoute
1256 // because it has a duplicate entry for members
1257 asyncResp->res.jsonValue["@odata.type"] =
1258 "#LogEntryCollection.LogEntryCollection";
1259 asyncResp->res.jsonValue["@odata.id"] =
1260 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1261 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1262 asyncResp->res.jsonValue["Description"] =
1263 "Collection of System Event Log Entries";
Jason M. Bills897967d2019-07-29 17:05:30 -07001264
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001265 nlohmann::json& logEntryArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001266 asyncResp->res.jsonValue["Members"];
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001267 logEntryArray = nlohmann::json::array();
1268 // Go through the log files and create a unique ID for each
1269 // entry
1270 std::vector<std::filesystem::path> redfishLogFiles;
1271 getRedfishLogFiles(redfishLogFiles);
1272 uint64_t entryCount = 0;
1273 std::string logEntry;
1274
1275 // Oldest logs are in the last file, so start there and loop
1276 // backwards
1277 for (auto it = redfishLogFiles.rbegin();
1278 it < redfishLogFiles.rend(); it++)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001279 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001280 std::ifstream logStream(*it);
1281 if (!logStream.is_open())
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001282 {
1283 continue;
1284 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001285
1286 // Reset the unique ID on the first entry
1287 bool firstEntry = true;
1288 while (std::getline(logStream, logEntry))
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001289 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001290 entryCount++;
1291 // Handle paging using skip (number of entries to skip
1292 // from the start) and top (number of entries to
1293 // display)
1294 if (entryCount <= skip || entryCount > skip + top)
George Liuebd45902020-08-26 14:21:10 +08001295 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001296 continue;
George Liuebd45902020-08-26 14:21:10 +08001297 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001298
1299 std::string idStr;
1300 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
George Liuebd45902020-08-26 14:21:10 +08001301 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001302 continue;
George Liuebd45902020-08-26 14:21:10 +08001303 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001304
1305 if (firstEntry)
1306 {
1307 firstEntry = false;
1308 }
1309
1310 logEntryArray.push_back({});
1311 nlohmann::json& bmcLogEntry = logEntryArray.back();
1312 if (fillEventLogEntryJson(idStr, logEntry,
1313 bmcLogEntry) != 0)
Xiaochao Ma75710de2021-01-21 17:56:02 +08001314 {
1315 messages::internalError(asyncResp->res);
1316 return;
1317 }
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001318 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001319 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001320 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1321 if (skip + top < entryCount)
Ed Tanous271584a2019-07-09 16:24:22 -07001322 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001323 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001324 "/redfish/v1/Systems/system/LogServices/EventLog/"
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001325 "Entries?$skip=" +
1326 std::to_string(skip + top);
Adriana Kobylakf86bb902021-01-11 11:11:05 -06001327 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001328 });
1329}
Chicago Duan336e96c2019-07-15 14:22:08 +08001330
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001331inline void requestRoutesJournalEventLogEntry(App& app)
1332{
1333 BMCWEB_ROUTE(
1334 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001335 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001336 .methods(boost::beast::http::verb::get)(
1337 [](const crow::Request&,
1338 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1339 const std::string& param) {
1340 const std::string& targetID = param;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001341
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001342 // Go through the log files and check the unique ID for each
1343 // entry to find the target entry
1344 std::vector<std::filesystem::path> redfishLogFiles;
1345 getRedfishLogFiles(redfishLogFiles);
1346 std::string logEntry;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001347
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001348 // Oldest logs are in the last file, so start there and loop
1349 // backwards
1350 for (auto it = redfishLogFiles.rbegin();
1351 it < redfishLogFiles.rend(); it++)
1352 {
1353 std::ifstream logStream(*it);
1354 if (!logStream.is_open())
1355 {
1356 continue;
1357 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001358
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001359 // Reset the unique ID on the first entry
1360 bool firstEntry = true;
1361 while (std::getline(logStream, logEntry))
1362 {
1363 std::string idStr;
1364 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1365 {
1366 continue;
1367 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001368
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001369 if (firstEntry)
1370 {
1371 firstEntry = false;
1372 }
Xiaochao Ma75710de2021-01-21 17:56:02 +08001373
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001374 if (idStr == targetID)
1375 {
1376 if (fillEventLogEntryJson(
1377 idStr, logEntry,
1378 asyncResp->res.jsonValue) != 0)
1379 {
1380 messages::internalError(asyncResp->res);
1381 return;
1382 }
1383 return;
1384 }
1385 }
1386 }
1387 // Requested ID was not found
1388 messages::resourceMissingAtURI(asyncResp->res, targetID);
1389 });
1390}
1391
1392inline void requestRoutesDBusEventLogEntryCollection(App& app)
1393{
1394 BMCWEB_ROUTE(app,
1395 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001396 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001397 .methods(
1398 boost::beast::http::verb::
1399 get)([](const crow::Request&,
1400 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1401 // Collections don't include the static data added by SubRoute
1402 // because it has a duplicate entry for members
1403 asyncResp->res.jsonValue["@odata.type"] =
1404 "#LogEntryCollection.LogEntryCollection";
1405 asyncResp->res.jsonValue["@odata.id"] =
1406 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1407 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1408 asyncResp->res.jsonValue["Description"] =
1409 "Collection of System Event Log Entries";
1410
1411 // DBus implementation of EventLog/Entries
1412 // Make call to Logging Service to find all log entry objects
Xiaochao Ma75710de2021-01-21 17:56:02 +08001413 crow::connections::systemBus->async_method_call(
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001414 [asyncResp](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001415 const dbus::utility::ManagedObjectType& resp) {
Xiaochao Ma75710de2021-01-21 17:56:02 +08001416 if (ec)
1417 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001418 // TODO Handle for specific error code
1419 BMCWEB_LOG_ERROR
1420 << "getLogEntriesIfaceData resp_handler got error "
1421 << ec;
Xiaochao Ma75710de2021-01-21 17:56:02 +08001422 messages::internalError(asyncResp->res);
1423 return;
1424 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001425 nlohmann::json& entriesArray =
1426 asyncResp->res.jsonValue["Members"];
1427 entriesArray = nlohmann::json::array();
1428 for (auto& objectPath : resp)
1429 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001430 const uint32_t* id = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001431 std::time_t timestamp{};
1432 std::time_t updateTimestamp{};
Ed Tanous914e2d52022-01-07 11:38:34 -08001433 const std::string* severity = nullptr;
1434 const std::string* message = nullptr;
1435 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001436 bool resolved = false;
1437 for (auto& interfaceMap : objectPath.second)
1438 {
1439 if (interfaceMap.first ==
1440 "xyz.openbmc_project.Logging.Entry")
1441 {
1442 for (auto& propertyMap : interfaceMap.second)
1443 {
1444 if (propertyMap.first == "Id")
1445 {
1446 id = std::get_if<uint32_t>(
1447 &propertyMap.second);
1448 }
1449 else if (propertyMap.first == "Timestamp")
1450 {
1451 const uint64_t* millisTimeStamp =
1452 std::get_if<uint64_t>(
1453 &propertyMap.second);
1454 if (millisTimeStamp != nullptr)
1455 {
1456 timestamp =
1457 crow::utility::getTimestamp(
1458 *millisTimeStamp);
1459 }
1460 }
1461 else if (propertyMap.first ==
1462 "UpdateTimestamp")
1463 {
1464 const uint64_t* millisTimeStamp =
1465 std::get_if<uint64_t>(
1466 &propertyMap.second);
1467 if (millisTimeStamp != nullptr)
1468 {
1469 updateTimestamp =
1470 crow::utility::getTimestamp(
1471 *millisTimeStamp);
1472 }
1473 }
1474 else if (propertyMap.first == "Severity")
1475 {
1476 severity = std::get_if<std::string>(
1477 &propertyMap.second);
1478 }
1479 else if (propertyMap.first == "Message")
1480 {
1481 message = std::get_if<std::string>(
1482 &propertyMap.second);
1483 }
1484 else if (propertyMap.first == "Resolved")
1485 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001486 const bool* resolveptr =
1487 std::get_if<bool>(
1488 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001489 if (resolveptr == nullptr)
1490 {
1491 messages::internalError(
1492 asyncResp->res);
1493 return;
1494 }
1495 resolved = *resolveptr;
1496 }
1497 }
1498 if (id == nullptr || message == nullptr ||
1499 severity == nullptr)
1500 {
1501 messages::internalError(asyncResp->res);
1502 return;
1503 }
1504 }
1505 else if (interfaceMap.first ==
1506 "xyz.openbmc_project.Common.FilePath")
1507 {
1508 for (auto& propertyMap : interfaceMap.second)
1509 {
1510 if (propertyMap.first == "Path")
1511 {
1512 filePath = std::get_if<std::string>(
1513 &propertyMap.second);
1514 }
1515 }
1516 }
1517 }
1518 // Object path without the
1519 // xyz.openbmc_project.Logging.Entry interface, ignore
1520 // and continue.
1521 if (id == nullptr || message == nullptr ||
1522 severity == nullptr)
1523 {
1524 continue;
1525 }
1526 entriesArray.push_back({});
1527 nlohmann::json& thisEntry = entriesArray.back();
1528 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1529 thisEntry["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001530 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001531 std::to_string(*id);
1532 thisEntry["Name"] = "System Event Log Entry";
1533 thisEntry["Id"] = std::to_string(*id);
1534 thisEntry["Message"] = *message;
1535 thisEntry["Resolved"] = resolved;
1536 thisEntry["EntryType"] = "Event";
1537 thisEntry["Severity"] =
1538 translateSeverityDbusToRedfish(*severity);
1539 thisEntry["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001540 crow::utility::getDateTimeStdtime(timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001541 thisEntry["Modified"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001542 crow::utility::getDateTimeStdtime(updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001543 if (filePath != nullptr)
1544 {
1545 thisEntry["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001546 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001547 std::to_string(*id) + "/attachment";
1548 }
1549 }
1550 std::sort(entriesArray.begin(), entriesArray.end(),
1551 [](const nlohmann::json& left,
1552 const nlohmann::json& right) {
1553 return (left["Id"] <= right["Id"]);
1554 });
1555 asyncResp->res.jsonValue["Members@odata.count"] =
1556 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001557 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001558 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1559 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1560 });
1561}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001562
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001563inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001564{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001565 BMCWEB_ROUTE(
1566 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001567 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001568 .methods(boost::beast::http::verb::get)(
1569 [](const crow::Request&,
1570 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1571 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001572
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001573 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001574 std::string entryID = param;
1575 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001576
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001577 // DBus implementation of EventLog/Entries
1578 // Make call to Logging Service to find all log entry objects
1579 crow::connections::systemBus->async_method_call(
1580 [asyncResp, entryID](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001581 const GetManagedPropertyType& resp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001582 if (ec.value() == EBADR)
1583 {
1584 messages::resourceNotFound(
1585 asyncResp->res, "EventLogEntry", entryID);
1586 return;
1587 }
1588 if (ec)
1589 {
George Liu0fda0f12021-11-16 10:06:17 +08001590 BMCWEB_LOG_ERROR
1591 << "EventLogEntry (DBus) resp_handler got error "
1592 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001593 messages::internalError(asyncResp->res);
1594 return;
1595 }
Ed Tanous914e2d52022-01-07 11:38:34 -08001596 const uint32_t* id = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001597 std::time_t timestamp{};
1598 std::time_t updateTimestamp{};
Ed Tanous914e2d52022-01-07 11:38:34 -08001599 const std::string* severity = nullptr;
1600 const std::string* message = nullptr;
1601 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001602 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001603
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001604 for (auto& propertyMap : resp)
1605 {
1606 if (propertyMap.first == "Id")
1607 {
1608 id = std::get_if<uint32_t>(&propertyMap.second);
1609 }
1610 else if (propertyMap.first == "Timestamp")
1611 {
1612 const uint64_t* millisTimeStamp =
1613 std::get_if<uint64_t>(&propertyMap.second);
1614 if (millisTimeStamp != nullptr)
1615 {
1616 timestamp = crow::utility::getTimestamp(
1617 *millisTimeStamp);
1618 }
1619 }
1620 else if (propertyMap.first == "UpdateTimestamp")
1621 {
1622 const uint64_t* millisTimeStamp =
1623 std::get_if<uint64_t>(&propertyMap.second);
1624 if (millisTimeStamp != nullptr)
1625 {
1626 updateTimestamp =
1627 crow::utility::getTimestamp(
1628 *millisTimeStamp);
1629 }
1630 }
1631 else if (propertyMap.first == "Severity")
1632 {
1633 severity = std::get_if<std::string>(
1634 &propertyMap.second);
1635 }
1636 else if (propertyMap.first == "Message")
1637 {
1638 message = std::get_if<std::string>(
1639 &propertyMap.second);
1640 }
1641 else if (propertyMap.first == "Resolved")
1642 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001643 const bool* resolveptr =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001644 std::get_if<bool>(&propertyMap.second);
1645 if (resolveptr == nullptr)
1646 {
1647 messages::internalError(asyncResp->res);
1648 return;
1649 }
1650 resolved = *resolveptr;
1651 }
1652 else if (propertyMap.first == "Path")
1653 {
1654 filePath = std::get_if<std::string>(
1655 &propertyMap.second);
1656 }
1657 }
1658 if (id == nullptr || message == nullptr ||
1659 severity == nullptr)
1660 {
1661 messages::internalError(asyncResp->res);
1662 return;
1663 }
1664 asyncResp->res.jsonValue["@odata.type"] =
1665 "#LogEntry.v1_8_0.LogEntry";
1666 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001667 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001668 std::to_string(*id);
1669 asyncResp->res.jsonValue["Name"] =
1670 "System Event Log Entry";
1671 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1672 asyncResp->res.jsonValue["Message"] = *message;
1673 asyncResp->res.jsonValue["Resolved"] = resolved;
1674 asyncResp->res.jsonValue["EntryType"] = "Event";
1675 asyncResp->res.jsonValue["Severity"] =
1676 translateSeverityDbusToRedfish(*severity);
1677 asyncResp->res.jsonValue["Created"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001678 crow::utility::getDateTimeStdtime(timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001679 asyncResp->res.jsonValue["Modified"] =
Nan Zhou1d8782e2021-11-29 22:23:18 -08001680 crow::utility::getDateTimeStdtime(updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001681 if (filePath != nullptr)
1682 {
1683 asyncResp->res.jsonValue["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001684 "/redfish/v1/Systems/system/LogServices/EventLog/attachment/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001685 std::to_string(*id);
1686 }
1687 },
1688 "xyz.openbmc_project.Logging",
1689 "/xyz/openbmc_project/logging/entry/" + entryID,
1690 "org.freedesktop.DBus.Properties", "GetAll", "");
1691 });
1692
1693 BMCWEB_ROUTE(
1694 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001695 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001696 .methods(boost::beast::http::verb::patch)(
1697 [](const crow::Request& req,
1698 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1699 const std::string& entryId) {
1700 std::optional<bool> resolved;
1701
1702 if (!json_util::readJson(req, asyncResp->res, "Resolved",
1703 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001704 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001705 return;
1706 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001707 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001708
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001709 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001710 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001711 if (ec)
1712 {
1713 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1714 messages::internalError(asyncResp->res);
1715 return;
1716 }
1717 },
1718 "xyz.openbmc_project.Logging",
1719 "/xyz/openbmc_project/logging/entry/" + entryId,
1720 "org.freedesktop.DBus.Properties", "Set",
1721 "xyz.openbmc_project.Logging.Entry", "Resolved",
Ed Tanous168e20c2021-12-13 14:39:53 -08001722 dbus::utility::DbusVariantType(*resolved));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001723 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001724
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001725 BMCWEB_ROUTE(
1726 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001727 .privileges(redfish::privileges::deleteLogEntry)
1728
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001729 .methods(boost::beast::http::verb::delete_)(
1730 [](const crow::Request&,
1731 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1732 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001733
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001734 {
1735 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001736
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001737 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001738
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001739 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001740
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001741 // Process response from Logging service.
1742 auto respHandler = [asyncResp, entryID](
1743 const boost::system::error_code ec) {
1744 BMCWEB_LOG_DEBUG
1745 << "EventLogEntry (DBus) doDelete callback: Done";
1746 if (ec)
1747 {
1748 if (ec.value() == EBADR)
1749 {
1750 messages::resourceNotFound(asyncResp->res,
1751 "LogEntry", entryID);
1752 return;
1753 }
1754 // TODO Handle for specific error code
George Liu0fda0f12021-11-16 10:06:17 +08001755 BMCWEB_LOG_ERROR
1756 << "EventLogEntry (DBus) doDelete respHandler got error "
1757 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001758 asyncResp->res.result(
1759 boost::beast::http::status::internal_server_error);
1760 return;
1761 }
1762
1763 asyncResp->res.result(boost::beast::http::status::ok);
1764 };
1765
1766 // Make call to Logging service to request Delete Log
1767 crow::connections::systemBus->async_method_call(
1768 respHandler, "xyz.openbmc_project.Logging",
1769 "/xyz/openbmc_project/logging/entry/" + entryID,
1770 "xyz.openbmc_project.Object.Delete", "Delete");
1771 });
1772}
1773
1774inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001775{
George Liu0fda0f12021-11-16 10:06:17 +08001776 BMCWEB_ROUTE(
1777 app,
1778 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001779 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001780 .methods(boost::beast::http::verb::get)(
1781 [](const crow::Request& req,
1782 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1783 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001784
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001785 {
George Liu647b3cd2021-07-05 12:43:56 +08001786 if (!http_helpers::isOctetAccepted(
1787 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001788 {
1789 asyncResp->res.result(
1790 boost::beast::http::status::bad_request);
1791 return;
1792 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001793
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001794 std::string entryID = param;
1795 dbus::utility::escapePathForDbus(entryID);
1796
1797 crow::connections::systemBus->async_method_call(
1798 [asyncResp,
1799 entryID](const boost::system::error_code ec,
1800 const sdbusplus::message::unix_fd& unixfd) {
1801 if (ec.value() == EBADR)
1802 {
1803 messages::resourceNotFound(
1804 asyncResp->res, "EventLogAttachment", entryID);
1805 return;
1806 }
1807 if (ec)
1808 {
1809 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1810 messages::internalError(asyncResp->res);
1811 return;
1812 }
1813
1814 int fd = -1;
1815 fd = dup(unixfd);
1816 if (fd == -1)
1817 {
1818 messages::internalError(asyncResp->res);
1819 return;
1820 }
1821
1822 long long int size = lseek(fd, 0, SEEK_END);
1823 if (size == -1)
1824 {
1825 messages::internalError(asyncResp->res);
1826 return;
1827 }
1828
1829 // Arbitrary max size of 64kb
1830 constexpr int maxFileSize = 65536;
1831 if (size > maxFileSize)
1832 {
1833 BMCWEB_LOG_ERROR
1834 << "File size exceeds maximum allowed size of "
1835 << maxFileSize;
1836 messages::internalError(asyncResp->res);
1837 return;
1838 }
1839 std::vector<char> data(static_cast<size_t>(size));
1840 long long int rc = lseek(fd, 0, SEEK_SET);
1841 if (rc == -1)
1842 {
1843 messages::internalError(asyncResp->res);
1844 return;
1845 }
1846 rc = read(fd, data.data(), data.size());
1847 if ((rc == -1) || (rc != size))
1848 {
1849 messages::internalError(asyncResp->res);
1850 return;
1851 }
1852 close(fd);
1853
1854 std::string_view strData(data.data(), data.size());
1855 std::string output =
1856 crow::utility::base64encode(strData);
1857
1858 asyncResp->res.addHeader("Content-Type",
1859 "application/octet-stream");
1860 asyncResp->res.addHeader("Content-Transfer-Encoding",
1861 "Base64");
1862 asyncResp->res.body() = std::move(output);
1863 },
1864 "xyz.openbmc_project.Logging",
1865 "/xyz/openbmc_project/logging/entry/" + entryID,
1866 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1867 });
1868}
1869
Spencer Kub7028eb2021-10-26 15:27:35 +08001870constexpr const char* hostLoggerFolderPath = "/var/log/console";
1871
1872inline bool
1873 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1874 std::vector<std::filesystem::path>& hostLoggerFiles)
1875{
1876 std::error_code ec;
1877 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1878 if (ec)
1879 {
1880 BMCWEB_LOG_ERROR << ec.message();
1881 return false;
1882 }
1883 for (const std::filesystem::directory_entry& it : logPath)
1884 {
1885 std::string filename = it.path().filename();
1886 // Prefix of each log files is "log". Find the file and save the
1887 // path
1888 if (boost::starts_with(filename, "log"))
1889 {
1890 hostLoggerFiles.emplace_back(it.path());
1891 }
1892 }
1893 // As the log files rotate, they are appended with a ".#" that is higher for
1894 // the older logs. Since we start from oldest logs, sort the name in
1895 // descending order.
1896 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1897 AlphanumLess<std::string>());
1898
1899 return true;
1900}
1901
1902inline bool
1903 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
1904 uint64_t& skip, uint64_t& top,
1905 std::vector<std::string>& logEntries, size_t& logCount)
1906{
1907 GzFileReader logFile;
1908
1909 // Go though all log files and expose host logs.
1910 for (const std::filesystem::path& it : hostLoggerFiles)
1911 {
1912 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1913 {
1914 BMCWEB_LOG_ERROR << "fail to expose host logs";
1915 return false;
1916 }
1917 }
1918 // Get lastMessage from constructor by getter
1919 std::string lastMessage = logFile.getLastMessage();
1920 if (!lastMessage.empty())
1921 {
1922 logCount++;
1923 if (logCount > skip && logCount <= (skip + top))
1924 {
1925 logEntries.push_back(lastMessage);
1926 }
1927 }
1928 return true;
1929}
1930
1931inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1932 const std::string& msg,
1933 nlohmann::json& logEntryJson)
1934{
1935 // Fill in the log entry with the gathered data.
1936 logEntryJson = {
1937 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1938 {"@odata.id",
1939 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1940 logEntryID},
1941 {"Name", "Host Logger Entry"},
1942 {"Id", logEntryID},
1943 {"Message", msg},
1944 {"EntryType", "Oem"},
1945 {"Severity", "OK"},
1946 {"OemRecordFormat", "Host Logger Entry"}};
1947}
1948
1949inline void requestRoutesSystemHostLogger(App& app)
1950{
1951 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1952 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08001953 .methods(
1954 boost::beast::http::verb::
1955 get)([](const crow::Request&,
1956 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1957 asyncResp->res.jsonValue["@odata.id"] =
1958 "/redfish/v1/Systems/system/LogServices/HostLogger";
1959 asyncResp->res.jsonValue["@odata.type"] =
1960 "#LogService.v1_1_0.LogService";
1961 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1962 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1963 asyncResp->res.jsonValue["Id"] = "HostLogger";
1964 asyncResp->res.jsonValue["Entries"] = {
1965 {"@odata.id",
1966 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries"}};
1967 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001968}
1969
1970inline void requestRoutesSystemHostLoggerCollection(App& app)
1971{
1972 BMCWEB_ROUTE(app,
1973 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1974 .privileges(redfish::privileges::getLogEntry)
George Liu0fda0f12021-11-16 10:06:17 +08001975 .methods(
1976 boost::beast::http::verb::
1977 get)([](const crow::Request& req,
1978 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1979 uint64_t skip = 0;
1980 uint64_t top = maxEntriesPerPage; // Show max 1000 entries by
1981 // default, allow range 1 to
1982 // 1000 entries per page.
1983 if (!getSkipParam(asyncResp, req, skip))
1984 {
1985 return;
1986 }
1987 if (!getTopParam(asyncResp, req, top))
1988 {
1989 return;
1990 }
1991 asyncResp->res.jsonValue["@odata.id"] =
1992 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1993 asyncResp->res.jsonValue["@odata.type"] =
1994 "#LogEntryCollection.LogEntryCollection";
1995 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1996 asyncResp->res.jsonValue["Description"] =
1997 "Collection of HostLogger Entries";
1998 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1999 logEntryArray = nlohmann::json::array();
2000 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08002001
George Liu0fda0f12021-11-16 10:06:17 +08002002 std::vector<std::filesystem::path> hostLoggerFiles;
2003 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2004 {
2005 BMCWEB_LOG_ERROR << "fail to get host log file path";
2006 return;
2007 }
2008
2009 size_t logCount = 0;
2010 // This vector only store the entries we want to expose that
2011 // control by skip and top.
2012 std::vector<std::string> logEntries;
2013 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
2014 logCount))
2015 {
2016 messages::internalError(asyncResp->res);
2017 return;
2018 }
2019 // If vector is empty, that means skip value larger than total
2020 // log count
Ed Tanous26f69762022-01-25 09:49:11 -08002021 if (logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08002022 {
2023 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
2024 return;
2025 }
Ed Tanous26f69762022-01-25 09:49:11 -08002026 if (!logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08002027 {
2028 for (size_t i = 0; i < logEntries.size(); i++)
Spencer Kub7028eb2021-10-26 15:27:35 +08002029 {
George Liu0fda0f12021-11-16 10:06:17 +08002030 logEntryArray.push_back({});
2031 nlohmann::json& hostLogEntry = logEntryArray.back();
2032 fillHostLoggerEntryJson(std::to_string(skip + i),
2033 logEntries[i], hostLogEntry);
Spencer Kub7028eb2021-10-26 15:27:35 +08002034 }
2035
George Liu0fda0f12021-11-16 10:06:17 +08002036 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
2037 if (skip + top < logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08002038 {
George Liu0fda0f12021-11-16 10:06:17 +08002039 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2040 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
2041 std::to_string(skip + top);
Spencer Kub7028eb2021-10-26 15:27:35 +08002042 }
George Liu0fda0f12021-11-16 10:06:17 +08002043 }
2044 });
Spencer Kub7028eb2021-10-26 15:27:35 +08002045}
2046
2047inline void requestRoutesSystemHostLoggerLogEntry(App& app)
2048{
2049 BMCWEB_ROUTE(
2050 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
2051 .privileges(redfish::privileges::getLogEntry)
2052 .methods(boost::beast::http::verb::get)(
2053 [](const crow::Request&,
2054 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2055 const std::string& param) {
2056 const std::string& targetID = param;
2057
2058 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08002059
2060 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
2061 const char* end = targetID.data() + targetID.size();
2062
2063 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
Spencer Kub7028eb2021-10-26 15:27:35 +08002064 if (ec == std::errc::invalid_argument)
2065 {
2066 messages::resourceMissingAtURI(asyncResp->res, targetID);
2067 return;
2068 }
2069 if (ec == std::errc::result_out_of_range)
2070 {
2071 messages::resourceMissingAtURI(asyncResp->res, targetID);
2072 return;
2073 }
2074
2075 std::vector<std::filesystem::path> hostLoggerFiles;
2076 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2077 {
2078 BMCWEB_LOG_ERROR << "fail to get host log file path";
2079 return;
2080 }
2081
2082 size_t logCount = 0;
2083 uint64_t top = 1;
2084 std::vector<std::string> logEntries;
2085 // We can get specific entry by skip and top. For example, if we
2086 // want to get nth entry, we can set skip = n-1 and top = 1 to
2087 // get that entry
2088 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
2089 logEntries, logCount))
2090 {
2091 messages::internalError(asyncResp->res);
2092 return;
2093 }
2094
2095 if (!logEntries.empty())
2096 {
2097 fillHostLoggerEntryJson(targetID, logEntries[0],
2098 asyncResp->res.jsonValue);
2099 return;
2100 }
2101
2102 // Requested ID was not found
2103 messages::resourceMissingAtURI(asyncResp->res, targetID);
2104 });
2105}
2106
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002107inline void requestRoutesBMCLogServiceCollection(App& app)
2108{
2109 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002110 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002111 .methods(boost::beast::http::verb::get)(
2112 [](const crow::Request&,
2113 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2114 // Collections don't include the static data added by SubRoute
2115 // because it has a duplicate entry for members
2116 asyncResp->res.jsonValue["@odata.type"] =
2117 "#LogServiceCollection.LogServiceCollection";
2118 asyncResp->res.jsonValue["@odata.id"] =
2119 "/redfish/v1/Managers/bmc/LogServices";
2120 asyncResp->res.jsonValue["Name"] =
2121 "Open BMC Log Services Collection";
2122 asyncResp->res.jsonValue["Description"] =
2123 "Collection of LogServices for this Manager";
2124 nlohmann::json& logServiceArray =
2125 asyncResp->res.jsonValue["Members"];
2126 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002127#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002128 logServiceArray.push_back(
2129 {{"@odata.id",
2130 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002131#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002132#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002133 logServiceArray.push_back(
2134 {{"@odata.id",
2135 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002136#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002137 asyncResp->res.jsonValue["Members@odata.count"] =
2138 logServiceArray.size();
2139 });
2140}
Ed Tanous1da66f72018-07-27 16:13:37 -07002141
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002142inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002143{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002144 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002145 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002146 .methods(boost::beast::http::verb::get)(
2147 [](const crow::Request&,
2148 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002149
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002150 {
2151 asyncResp->res.jsonValue["@odata.type"] =
2152 "#LogService.v1_1_0.LogService";
2153 asyncResp->res.jsonValue["@odata.id"] =
2154 "/redfish/v1/Managers/bmc/LogServices/Journal";
2155 asyncResp->res.jsonValue["Name"] =
2156 "Open BMC Journal Log Service";
2157 asyncResp->res.jsonValue["Description"] =
2158 "BMC Journal Log Service";
2159 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2160 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302161
2162 std::pair<std::string, std::string> redfishDateTimeOffset =
2163 crow::utility::getDateTimeOffsetNow();
2164 asyncResp->res.jsonValue["DateTime"] =
2165 redfishDateTimeOffset.first;
2166 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2167 redfishDateTimeOffset.second;
2168
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002169 asyncResp->res.jsonValue["Entries"] = {
2170 {"@odata.id",
2171 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
2172 });
2173}
Jason M. Billse1f26342018-07-18 12:12:00 -07002174
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002175static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2176 sd_journal* journal,
2177 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002178{
2179 // Get the Log Entry contents
2180 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002181
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002182 std::string message;
2183 std::string_view syslogID;
2184 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2185 if (ret < 0)
2186 {
2187 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2188 << strerror(-ret);
2189 }
2190 if (!syslogID.empty())
2191 {
2192 message += std::string(syslogID) + ": ";
2193 }
2194
Ed Tanous39e77502019-03-04 17:35:53 -08002195 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002196 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002197 if (ret < 0)
2198 {
2199 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2200 return 1;
2201 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002202 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002203
2204 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002205 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002206 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002207 if (ret < 0)
2208 {
2209 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002210 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002211
2212 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002213 std::string entryTimeStr;
2214 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002215 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002216 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002217 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002218
2219 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002220 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002221 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002222 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2223 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002224 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002225 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002226 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002227 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002228 {"Severity", severity <= 2 ? "Critical"
2229 : severity <= 4 ? "Warning"
2230 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002231 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002232 {"Created", std::move(entryTimeStr)}};
2233 return 0;
2234}
2235
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002236inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002237{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002238 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002239 .privileges(redfish::privileges::getLogEntryCollection)
George Liu0fda0f12021-11-16 10:06:17 +08002240 .methods(
2241 boost::beast::http::verb::
2242 get)([](const crow::Request& req,
2243 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2244 static constexpr const long maxEntriesPerPage = 1000;
2245 uint64_t skip = 0;
2246 uint64_t top = maxEntriesPerPage; // Show max entries by default
2247 if (!getSkipParam(asyncResp, req, skip))
2248 {
2249 return;
2250 }
2251 if (!getTopParam(asyncResp, req, top))
2252 {
2253 return;
2254 }
2255 // Collections don't include the static data added by SubRoute
2256 // because it has a duplicate entry for members
2257 asyncResp->res.jsonValue["@odata.type"] =
2258 "#LogEntryCollection.LogEntryCollection";
2259 asyncResp->res.jsonValue["@odata.id"] =
2260 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2261 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2262 asyncResp->res.jsonValue["Description"] =
2263 "Collection of BMC Journal Entries";
2264 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2265 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002266
George Liu0fda0f12021-11-16 10:06:17 +08002267 // Go through the journal and use the timestamp to create a
2268 // unique ID for each entry
2269 sd_journal* journalTmp = nullptr;
2270 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2271 if (ret < 0)
2272 {
2273 BMCWEB_LOG_ERROR << "failed to open journal: "
2274 << strerror(-ret);
2275 messages::internalError(asyncResp->res);
2276 return;
2277 }
2278 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2279 journalTmp, sd_journal_close);
2280 journalTmp = nullptr;
2281 uint64_t entryCount = 0;
2282 // Reset the unique ID on the first entry
2283 bool firstEntry = true;
2284 SD_JOURNAL_FOREACH(journal.get())
2285 {
2286 entryCount++;
2287 // Handle paging using skip (number of entries to skip from
2288 // the start) and top (number of entries to display)
2289 if (entryCount <= skip || entryCount > skip + top)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002290 {
George Liu0fda0f12021-11-16 10:06:17 +08002291 continue;
2292 }
2293
2294 std::string idStr;
2295 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2296 {
2297 continue;
2298 }
2299
2300 if (firstEntry)
2301 {
2302 firstEntry = false;
2303 }
2304
2305 logEntryArray.push_back({});
2306 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2307 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2308 bmcJournalLogEntry) != 0)
2309 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002310 messages::internalError(asyncResp->res);
2311 return;
2312 }
George Liu0fda0f12021-11-16 10:06:17 +08002313 }
2314 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2315 if (skip + top < entryCount)
2316 {
2317 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2318 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2319 std::to_string(skip + top);
2320 }
2321 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002322}
Jason M. Billse1f26342018-07-18 12:12:00 -07002323
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002324inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002325{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002326 BMCWEB_ROUTE(app,
2327 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002328 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002329 .methods(boost::beast::http::verb::get)(
2330 [](const crow::Request&,
2331 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2332 const std::string& entryID) {
2333 // Convert the unique ID back to a timestamp to find the entry
2334 uint64_t ts = 0;
2335 uint64_t index = 0;
2336 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2337 {
2338 return;
2339 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002340
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002341 sd_journal* journalTmp = nullptr;
2342 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2343 if (ret < 0)
2344 {
2345 BMCWEB_LOG_ERROR << "failed to open journal: "
2346 << strerror(-ret);
2347 messages::internalError(asyncResp->res);
2348 return;
2349 }
2350 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2351 journal(journalTmp, sd_journal_close);
2352 journalTmp = nullptr;
2353 // Go to the timestamp in the log and move to the entry at the
2354 // index tracking the unique ID
2355 std::string idStr;
2356 bool firstEntry = true;
2357 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2358 if (ret < 0)
2359 {
2360 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2361 << strerror(-ret);
2362 messages::internalError(asyncResp->res);
2363 return;
2364 }
2365 for (uint64_t i = 0; i <= index; i++)
2366 {
2367 sd_journal_next(journal.get());
2368 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2369 {
2370 messages::internalError(asyncResp->res);
2371 return;
2372 }
2373 if (firstEntry)
2374 {
2375 firstEntry = false;
2376 }
2377 }
2378 // Confirm that the entry ID matches what was requested
2379 if (idStr != entryID)
2380 {
2381 messages::resourceMissingAtURI(asyncResp->res, entryID);
2382 return;
2383 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002384
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002385 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2386 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002387 {
2388 messages::internalError(asyncResp->res);
2389 return;
2390 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002391 });
2392}
2393
2394inline void requestRoutesBMCDumpService(App& app)
2395{
2396 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002397 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08002398 .methods(
2399 boost::beast::http::verb::
2400 get)([](const crow::Request&,
2401 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2402 asyncResp->res.jsonValue["@odata.id"] =
2403 "/redfish/v1/Managers/bmc/LogServices/Dump";
2404 asyncResp->res.jsonValue["@odata.type"] =
2405 "#LogService.v1_2_0.LogService";
2406 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2407 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2408 asyncResp->res.jsonValue["Id"] = "Dump";
2409 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302410
George Liu0fda0f12021-11-16 10:06:17 +08002411 std::pair<std::string, std::string> redfishDateTimeOffset =
2412 crow::utility::getDateTimeOffsetNow();
2413 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2414 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2415 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302416
George Liu0fda0f12021-11-16 10:06:17 +08002417 asyncResp->res.jsonValue["Entries"] = {
2418 {"@odata.id",
2419 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2420 asyncResp->res.jsonValue["Actions"] = {
2421 {"#LogService.ClearLog",
2422 {{"target",
2423 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog"}}},
2424 {"#LogService.CollectDiagnosticData",
2425 {{"target",
2426 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
2427 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002428}
2429
2430inline void requestRoutesBMCDumpEntryCollection(App& app)
2431{
2432
2433 /**
2434 * Functions triggers appropriate requests on DBus
2435 */
2436 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002437 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002438 .methods(boost::beast::http::verb::get)(
2439 [](const crow::Request&,
2440 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2441 asyncResp->res.jsonValue["@odata.type"] =
2442 "#LogEntryCollection.LogEntryCollection";
2443 asyncResp->res.jsonValue["@odata.id"] =
2444 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2445 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2446 asyncResp->res.jsonValue["Description"] =
2447 "Collection of BMC Dump Entries";
2448
2449 getDumpEntryCollection(asyncResp, "BMC");
2450 });
2451}
2452
2453inline void requestRoutesBMCDumpEntry(App& app)
2454{
2455 BMCWEB_ROUTE(app,
2456 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002457 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002458 .methods(boost::beast::http::verb::get)(
2459 [](const crow::Request&,
2460 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2461 const std::string& param) {
2462 getDumpEntryById(asyncResp, param, "BMC");
2463 });
2464 BMCWEB_ROUTE(app,
2465 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002466 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002467 .methods(boost::beast::http::verb::delete_)(
2468 [](const crow::Request&,
2469 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2470 const std::string& param) {
2471 deleteDumpEntry(asyncResp, param, "bmc");
2472 });
2473}
2474
2475inline void requestRoutesBMCDumpCreate(App& app)
2476{
2477
George Liu0fda0f12021-11-16 10:06:17 +08002478 BMCWEB_ROUTE(
2479 app,
2480 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002481 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002482 .methods(boost::beast::http::verb::post)(
2483 [](const crow::Request& req,
2484 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2485 createDump(asyncResp, req, "BMC");
2486 });
2487}
2488
2489inline void requestRoutesBMCDumpClear(App& app)
2490{
George Liu0fda0f12021-11-16 10:06:17 +08002491 BMCWEB_ROUTE(
2492 app,
2493 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002494 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002495 .methods(boost::beast::http::verb::post)(
2496 [](const crow::Request&,
2497 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2498 clearDump(asyncResp, "BMC");
2499 });
2500}
2501
2502inline void requestRoutesSystemDumpService(App& app)
2503{
2504 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002505 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002506 .methods(boost::beast::http::verb::get)(
2507 [](const crow::Request&,
2508 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2509
2510 {
2511 asyncResp->res.jsonValue["@odata.id"] =
2512 "/redfish/v1/Systems/system/LogServices/Dump";
2513 asyncResp->res.jsonValue["@odata.type"] =
2514 "#LogService.v1_2_0.LogService";
2515 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2516 asyncResp->res.jsonValue["Description"] =
2517 "System Dump LogService";
2518 asyncResp->res.jsonValue["Id"] = "Dump";
2519 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302520
2521 std::pair<std::string, std::string> redfishDateTimeOffset =
2522 crow::utility::getDateTimeOffsetNow();
2523 asyncResp->res.jsonValue["DateTime"] =
2524 redfishDateTimeOffset.first;
2525 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2526 redfishDateTimeOffset.second;
2527
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002528 asyncResp->res.jsonValue["Entries"] = {
2529 {"@odata.id",
2530 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2531 asyncResp->res.jsonValue["Actions"] = {
2532 {"#LogService.ClearLog",
2533 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002534 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002535 {"#LogService.CollectDiagnosticData",
2536 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002537 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002538 });
2539}
2540
2541inline void requestRoutesSystemDumpEntryCollection(App& app)
2542{
2543
2544 /**
2545 * Functions triggers appropriate requests on DBus
2546 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002547 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002548 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002549 .methods(boost::beast::http::verb::get)(
2550 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002551 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002552 asyncResp->res.jsonValue["@odata.type"] =
2553 "#LogEntryCollection.LogEntryCollection";
2554 asyncResp->res.jsonValue["@odata.id"] =
2555 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2556 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2557 asyncResp->res.jsonValue["Description"] =
2558 "Collection of System Dump Entries";
2559
2560 getDumpEntryCollection(asyncResp, "System");
2561 });
2562}
2563
2564inline void requestRoutesSystemDumpEntry(App& app)
2565{
2566 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002567 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002568 .privileges(redfish::privileges::getLogEntry)
2569
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002570 .methods(boost::beast::http::verb::get)(
2571 [](const crow::Request&,
2572 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2573 const std::string& param) {
2574 getDumpEntryById(asyncResp, param, "System");
2575 });
2576
2577 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002578 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002579 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002580 .methods(boost::beast::http::verb::delete_)(
2581 [](const crow::Request&,
2582 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2583 const std::string& param) {
2584 deleteDumpEntry(asyncResp, param, "system");
2585 });
2586}
2587
2588inline void requestRoutesSystemDumpCreate(App& app)
2589{
George Liu0fda0f12021-11-16 10:06:17 +08002590 BMCWEB_ROUTE(
2591 app,
2592 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002593 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002594 .methods(boost::beast::http::verb::post)(
2595 [](const crow::Request& req,
2596 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2597
2598 { createDump(asyncResp, req, "System"); });
2599}
2600
2601inline void requestRoutesSystemDumpClear(App& app)
2602{
George Liu0fda0f12021-11-16 10:06:17 +08002603 BMCWEB_ROUTE(
2604 app,
2605 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002606 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002607 .methods(boost::beast::http::verb::post)(
2608 [](const crow::Request&,
2609 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2610
2611 { clearDump(asyncResp, "System"); });
2612}
2613
2614inline void requestRoutesCrashdumpService(App& app)
2615{
2616 // Note: Deviated from redfish privilege registry for GET & HEAD
2617 // method for security reasons.
2618 /**
2619 * Functions triggers appropriate requests on DBus
2620 */
2621 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002622 // This is incorrect, should be:
2623 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002624 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002625 .methods(
2626 boost::beast::http::verb::
2627 get)([](const crow::Request&,
2628 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2629 // Copy over the static data to include the entries added by
2630 // SubRoute
2631 asyncResp->res.jsonValue["@odata.id"] =
2632 "/redfish/v1/Systems/system/LogServices/Crashdump";
2633 asyncResp->res.jsonValue["@odata.type"] =
2634 "#LogService.v1_2_0.LogService";
2635 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2636 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2637 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2638 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2639 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302640
2641 std::pair<std::string, std::string> redfishDateTimeOffset =
2642 crow::utility::getDateTimeOffsetNow();
2643 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2644 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2645 redfishDateTimeOffset.second;
2646
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002647 asyncResp->res.jsonValue["Entries"] = {
2648 {"@odata.id",
2649 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2650 asyncResp->res.jsonValue["Actions"] = {
2651 {"#LogService.ClearLog",
George Liu0fda0f12021-11-16 10:06:17 +08002652 {{"target",
2653 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002654 {"#LogService.CollectDiagnosticData",
George Liu0fda0f12021-11-16 10:06:17 +08002655 {{"target",
2656 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002657 });
2658}
2659
2660void inline requestRoutesCrashdumpClear(App& app)
2661{
George Liu0fda0f12021-11-16 10:06:17 +08002662 BMCWEB_ROUTE(
2663 app,
2664 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002665 // This is incorrect, should be:
2666 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002667 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002668 .methods(boost::beast::http::verb::post)(
2669 [](const crow::Request&,
2670 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2671 crow::connections::systemBus->async_method_call(
2672 [asyncResp](const boost::system::error_code ec,
2673 const std::string&) {
2674 if (ec)
2675 {
2676 messages::internalError(asyncResp->res);
2677 return;
2678 }
2679 messages::success(asyncResp->res);
2680 },
2681 crashdumpObject, crashdumpPath, deleteAllInterface,
2682 "DeleteAll");
2683 });
2684}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002685
zhanghch058d1b46d2021-04-01 11:18:24 +08002686static void
2687 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2688 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002689{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002690 auto getStoredLogCallback =
2691 [asyncResp, logID, &logEntryJson](
2692 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002693 const std::vector<
2694 std::pair<std::string, dbus::utility::DbusVariantType>>&
2695 params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002696 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002697 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002698 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2699 if (ec.value() ==
2700 boost::system::linux_error::bad_request_descriptor)
2701 {
2702 messages::resourceNotFound(asyncResp->res, "LogEntry",
2703 logID);
2704 }
2705 else
2706 {
2707 messages::internalError(asyncResp->res);
2708 }
2709 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002710 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002711
Johnathan Mantey043a0532020-03-10 17:15:28 -07002712 std::string timestamp{};
2713 std::string filename{};
2714 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002715 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002716
2717 if (filename.empty() || timestamp.empty())
2718 {
2719 messages::resourceMissingAtURI(asyncResp->res, logID);
2720 return;
2721 }
2722
2723 std::string crashdumpURI =
2724 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2725 logID + "/" + filename;
Ed Tanousd0dbeef2021-07-01 08:46:46 -07002726 logEntryJson = {{"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002727 {"@odata.id", "/redfish/v1/Systems/system/"
2728 "LogServices/Crashdump/Entries/" +
2729 logID},
2730 {"Name", "CPU Crashdump"},
2731 {"Id", logID},
2732 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002733 {"AdditionalDataURI", std::move(crashdumpURI)},
2734 {"DiagnosticDataType", "OEM"},
2735 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002736 {"Created", std::move(timestamp)}};
2737 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002738 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002739 std::move(getStoredLogCallback), crashdumpObject,
2740 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002741 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002742}
2743
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002744inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002745{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002746 // Note: Deviated from redfish privilege registry for GET & HEAD
2747 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002748 /**
2749 * Functions triggers appropriate requests on DBus
2750 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002751 BMCWEB_ROUTE(app,
2752 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002753 // This is incorrect, should be.
2754 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002755 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002756 .methods(
2757 boost::beast::http::verb::
2758 get)([](const crow::Request&,
2759 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2760 // Collections don't include the static data added by SubRoute
2761 // because it has a duplicate entry for members
2762 auto getLogEntriesCallback = [asyncResp](
2763 const boost::system::error_code ec,
2764 const std::vector<std::string>&
2765 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002766 if (ec)
2767 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002768 if (ec.value() !=
2769 boost::system::errc::no_such_file_or_directory)
2770 {
2771 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2772 << ec.message();
2773 messages::internalError(asyncResp->res);
2774 return;
2775 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002776 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002777 asyncResp->res.jsonValue["@odata.type"] =
2778 "#LogEntryCollection.LogEntryCollection";
2779 asyncResp->res.jsonValue["@odata.id"] =
2780 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2781 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2782 asyncResp->res.jsonValue["Description"] =
2783 "Collection of Crashdump Entries";
2784 nlohmann::json& logEntryArray =
2785 asyncResp->res.jsonValue["Members"];
2786 logEntryArray = nlohmann::json::array();
2787 std::vector<std::string> logIDs;
2788 // Get the list of log entries and build up an empty array big
2789 // enough to hold them
2790 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002791 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002792 // Get the log ID
2793 std::size_t lastPos = objpath.rfind('/');
2794 if (lastPos == std::string::npos)
2795 {
2796 continue;
2797 }
2798 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002799
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002800 // Add a space for the log entry to the array
2801 logEntryArray.push_back({});
2802 }
2803 // Now go through and set up async calls to fill in the entries
2804 size_t index = 0;
2805 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002806 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002807 // Add the log entry to the array
2808 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002809 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002810 asyncResp->res.jsonValue["Members@odata.count"] =
2811 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002812 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002813 crow::connections::systemBus->async_method_call(
2814 std::move(getLogEntriesCallback),
2815 "xyz.openbmc_project.ObjectMapper",
2816 "/xyz/openbmc_project/object_mapper",
2817 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2818 std::array<const char*, 1>{crashdumpInterface});
2819 });
2820}
Ed Tanous1da66f72018-07-27 16:13:37 -07002821
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002822inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002823{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002824 // Note: Deviated from redfish privilege registry for GET & HEAD
2825 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002826
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002827 BMCWEB_ROUTE(
2828 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002829 // this is incorrect, should be
2830 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002831 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002832 .methods(boost::beast::http::verb::get)(
2833 [](const crow::Request&,
2834 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2835 const std::string& param) {
2836 const std::string& logID = param;
2837 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2838 });
2839}
Ed Tanous1da66f72018-07-27 16:13:37 -07002840
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002841inline void requestRoutesCrashdumpFile(App& app)
2842{
2843 // Note: Deviated from redfish privilege registry for GET & HEAD
2844 // method for security reasons.
2845 BMCWEB_ROUTE(
2846 app,
2847 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002848 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002849 .methods(boost::beast::http::verb::get)(
2850 [](const crow::Request&,
2851 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2852 const std::string& logID, const std::string& fileName) {
2853 auto getStoredLogCallback =
2854 [asyncResp, logID, fileName](
2855 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002856 const std::vector<std::pair<
2857 std::string, dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002858 resp) {
2859 if (ec)
2860 {
2861 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2862 << ec.message();
2863 messages::internalError(asyncResp->res);
2864 return;
2865 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002866
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002867 std::string dbusFilename{};
2868 std::string dbusTimestamp{};
2869 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002870
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002871 parseCrashdumpParameters(resp, dbusFilename,
2872 dbusTimestamp, dbusFilepath);
2873
2874 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2875 dbusFilepath.empty())
2876 {
2877 messages::resourceMissingAtURI(asyncResp->res,
2878 fileName);
2879 return;
2880 }
2881
2882 // Verify the file name parameter is correct
2883 if (fileName != dbusFilename)
2884 {
2885 messages::resourceMissingAtURI(asyncResp->res,
2886 fileName);
2887 return;
2888 }
2889
2890 if (!std::filesystem::exists(dbusFilepath))
2891 {
2892 messages::resourceMissingAtURI(asyncResp->res,
2893 fileName);
2894 return;
2895 }
Jason M. Bills2d314912022-01-12 13:59:01 -08002896 std::ifstream ifs(dbusFilepath,
2897 std::ios::in | std::ios::binary);
2898 asyncResp->res.body() = std::string(
2899 std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002900
2901 // Configure this to be a file download when accessed
2902 // from a browser
2903 asyncResp->res.addHeader("Content-Disposition",
2904 "attachment");
2905 };
2906 crow::connections::systemBus->async_method_call(
2907 std::move(getStoredLogCallback), crashdumpObject,
2908 crashdumpPath + std::string("/") + logID,
2909 "org.freedesktop.DBus.Properties", "GetAll",
2910 crashdumpInterface);
2911 });
2912}
2913
2914inline void requestRoutesCrashdumpCollect(App& app)
2915{
2916 // Note: Deviated from redfish privilege registry for GET & HEAD
2917 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002918 BMCWEB_ROUTE(
2919 app,
2920 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002921 // The below is incorrect; Should be ConfigureManager
2922 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002923 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002924 .methods(
2925 boost::beast::http::verb::
2926 post)([](const crow::Request& req,
2927 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2928 std::string diagnosticDataType;
2929 std::string oemDiagnosticDataType;
2930 if (!redfish::json_util::readJson(
2931 req, asyncResp->res, "DiagnosticDataType",
2932 diagnosticDataType, "OEMDiagnosticDataType",
2933 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002934 {
James Feist46229572020-02-19 15:11:58 -08002935 return;
2936 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002937
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002938 if (diagnosticDataType != "OEM")
2939 {
2940 BMCWEB_LOG_ERROR
2941 << "Only OEM DiagnosticDataType supported for Crashdump";
2942 messages::actionParameterValueFormatError(
2943 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2944 "CollectDiagnosticData");
2945 return;
2946 }
2947
Ed Tanous98be3e32021-09-16 15:05:36 -07002948 auto collectCrashdumpCallback = [asyncResp,
2949 payload(task::Payload(req))](
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002950 const boost::system::error_code
2951 ec,
Ed Tanous98be3e32021-09-16 15:05:36 -07002952 const std::string&) mutable {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002953 if (ec)
2954 {
2955 if (ec.value() ==
2956 boost::system::errc::operation_not_supported)
2957 {
2958 messages::resourceInStandby(asyncResp->res);
2959 }
2960 else if (ec.value() ==
2961 boost::system::errc::device_or_resource_busy)
2962 {
2963 messages::serviceTemporarilyUnavailable(asyncResp->res,
2964 "60");
2965 }
2966 else
2967 {
2968 messages::internalError(asyncResp->res);
2969 }
2970 return;
2971 }
George Liu0fda0f12021-11-16 10:06:17 +08002972 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2973 [](boost::system::error_code err,
2974 sdbusplus::message::message&,
2975 const std::shared_ptr<task::TaskData>& taskData) {
2976 if (!err)
2977 {
2978 taskData->messages.emplace_back(
2979 messages::taskCompletedOK(
2980 std::to_string(taskData->index)));
2981 taskData->state = "Completed";
2982 }
2983 return task::completed;
2984 },
2985 "type='signal',interface='org.freedesktop.DBus."
2986 "Properties',"
2987 "member='PropertiesChanged',arg0namespace='com.intel.crashdump'");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002988 task->startTimer(std::chrono::minutes(5));
2989 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -07002990 task->payload.emplace(std::move(payload));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002991 };
2992
2993 if (oemDiagnosticDataType == "OnDemand")
2994 {
2995 crow::connections::systemBus->async_method_call(
2996 std::move(collectCrashdumpCallback), crashdumpObject,
2997 crashdumpPath, crashdumpOnDemandInterface,
2998 "GenerateOnDemandLog");
2999 }
3000 else if (oemDiagnosticDataType == "Telemetry")
3001 {
3002 crow::connections::systemBus->async_method_call(
3003 std::move(collectCrashdumpCallback), crashdumpObject,
3004 crashdumpPath, crashdumpTelemetryInterface,
3005 "GenerateTelemetryLog");
3006 }
3007 else
3008 {
3009 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3010 << oemDiagnosticDataType;
3011 messages::actionParameterValueFormatError(
3012 asyncResp->res, oemDiagnosticDataType,
3013 "OEMDiagnosticDataType", "CollectDiagnosticData");
3014 return;
3015 }
3016 });
3017}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003018
Andrew Geisslercb92c032018-08-17 07:56:14 -07003019/**
3020 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3021 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003022inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003023{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003024 /**
3025 * Function handles POST method request.
3026 * The Clear Log actions does not require any parameter.The action deletes
3027 * all entries found in the Entries collection for this Log Service.
3028 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003029
George Liu0fda0f12021-11-16 10:06:17 +08003030 BMCWEB_ROUTE(
3031 app,
3032 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003033 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003034 .methods(boost::beast::http::verb::post)(
3035 [](const crow::Request&,
3036 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3037 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003038
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003039 // Process response from Logging service.
3040 auto respHandler = [asyncResp](
3041 const boost::system::error_code ec) {
3042 BMCWEB_LOG_DEBUG
3043 << "doClearLog resp_handler callback: Done";
3044 if (ec)
3045 {
3046 // TODO Handle for specific error code
3047 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
3048 << ec;
3049 asyncResp->res.result(
3050 boost::beast::http::status::internal_server_error);
3051 return;
3052 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003053
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003054 asyncResp->res.result(
3055 boost::beast::http::status::no_content);
3056 };
3057
3058 // Make call to Logging service to request Clear Log
3059 crow::connections::systemBus->async_method_call(
3060 respHandler, "xyz.openbmc_project.Logging",
3061 "/xyz/openbmc_project/logging",
3062 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3063 });
3064}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003065
3066/****************************************************
3067 * Redfish PostCode interfaces
3068 * using DBUS interface: getPostCodesTS
3069 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003070inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003071{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003072 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003073 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08003074 .methods(
3075 boost::beast::http::verb::
3076 get)([](const crow::Request&,
3077 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3078 asyncResp->res.jsonValue = {
3079 {"@odata.id",
3080 "/redfish/v1/Systems/system/LogServices/PostCodes"},
3081 {"@odata.type", "#LogService.v1_1_0.LogService"},
3082 {"Name", "POST Code Log Service"},
3083 {"Description", "POST Code Log Service"},
3084 {"Id", "BIOS POST Code Log"},
3085 {"OverWritePolicy", "WrapsWhenFull"},
3086 {"Entries",
3087 {{"@odata.id",
3088 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05303089
George Liu0fda0f12021-11-16 10:06:17 +08003090 std::pair<std::string, std::string> redfishDateTimeOffset =
3091 crow::utility::getDateTimeOffsetNow();
3092 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3093 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3094 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303095
George Liu0fda0f12021-11-16 10:06:17 +08003096 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3097 {"target",
3098 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3099 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003100}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003101
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003102inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003103{
George Liu0fda0f12021-11-16 10:06:17 +08003104 BMCWEB_ROUTE(
3105 app,
3106 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003107 // The following privilege is incorrect; It should be ConfigureManager
3108 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003109 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003110 .methods(boost::beast::http::verb::post)(
3111 [](const crow::Request&,
3112 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3113 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003114
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003115 // Make call to post-code service to request clear all
3116 crow::connections::systemBus->async_method_call(
3117 [asyncResp](const boost::system::error_code ec) {
3118 if (ec)
3119 {
3120 // TODO Handle for specific error code
3121 BMCWEB_LOG_ERROR
3122 << "doClearPostCodes resp_handler got error "
3123 << ec;
3124 asyncResp->res.result(boost::beast::http::status::
3125 internal_server_error);
3126 messages::internalError(asyncResp->res);
3127 return;
3128 }
3129 },
3130 "xyz.openbmc_project.State.Boot.PostCode0",
3131 "/xyz/openbmc_project/State/Boot/PostCode0",
3132 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3133 });
3134}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003135
3136static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003137 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303138 const boost::container::flat_map<
3139 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003140 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3141 const uint64_t skip = 0, const uint64_t top = 0)
3142{
3143 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003144 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05303145 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003146
3147 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003148 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003149
3150 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303151 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3152 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003153 {
3154 currentCodeIndex++;
3155 std::string postcodeEntryID =
3156 "B" + std::to_string(bootIndex) + "-" +
3157 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3158
3159 uint64_t usecSinceEpoch = code.first;
3160 uint64_t usTimeOffset = 0;
3161
3162 if (1 == currentCodeIndex)
3163 { // already incremented
3164 firstCodeTimeUs = code.first;
3165 }
3166 else
3167 {
3168 usTimeOffset = code.first - firstCodeTimeUs;
3169 }
3170
3171 // skip if no specific codeIndex is specified and currentCodeIndex does
3172 // not fall between top and skip
3173 if ((codeIndex == 0) &&
3174 (currentCodeIndex <= skip || currentCodeIndex > top))
3175 {
3176 continue;
3177 }
3178
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003179 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003180 // currentIndex
3181 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3182 {
3183 // This is done for simplicity. 1st entry is needed to calculate
3184 // time offset. To improve efficiency, one can get to the entry
3185 // directly (possibly with flatmap's nth method)
3186 continue;
3187 }
3188
3189 // currentCodeIndex is within top and skip or equal to specified code
3190 // index
3191
3192 // Get the Created time from the timestamp
3193 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003194 entryTimeStr =
3195 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003196
3197 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3198 std::ostringstream hexCode;
3199 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303200 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003201 std::ostringstream timeOffsetStr;
3202 // Set Fixed -Point Notation
3203 timeOffsetStr << std::fixed;
3204 // Set precision to 4 digits
3205 timeOffsetStr << std::setprecision(4);
3206 // Add double to stream
3207 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3208 std::vector<std::string> messageArgs = {
3209 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3210
3211 // Get MessageArgs template from message registry
3212 std::string msg;
3213 if (message != nullptr)
3214 {
3215 msg = message->message;
3216
3217 // fill in this post code value
3218 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003219 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003220 {
3221 std::string argStr = "%" + std::to_string(++i);
3222 size_t argPos = msg.find(argStr);
3223 if (argPos != std::string::npos)
3224 {
3225 msg.replace(argPos, argStr.length(), messageArg);
3226 }
3227 }
3228 }
3229
Tim Leed4342a92020-04-27 11:47:58 +08003230 // Get Severity template from message registry
3231 std::string severity;
3232 if (message != nullptr)
3233 {
3234 severity = message->severity;
3235 }
3236
ZhikuiRena3316fc2020-01-29 14:58:08 -08003237 // add to AsyncResp
3238 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003239 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003240 bmcLogEntry = {
3241 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3242 {"@odata.id",
3243 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3244 postcodeEntryID},
3245 {"Name", "POST Code Log Entry"},
3246 {"Id", postcodeEntryID},
3247 {"Message", std::move(msg)},
3248 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3249 {"MessageArgs", std::move(messageArgs)},
3250 {"EntryType", "Event"},
3251 {"Severity", std::move(severity)},
3252 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003253 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3254 {
3255 bmcLogEntry["AdditionalDataURI"] =
3256 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3257 postcodeEntryID + "/attachment";
3258 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003259 }
3260}
3261
zhanghch058d1b46d2021-04-01 11:18:24 +08003262static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003263 const uint16_t bootIndex,
3264 const uint64_t codeIndex)
3265{
3266 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303267 [aResp, bootIndex,
3268 codeIndex](const boost::system::error_code ec,
3269 const boost::container::flat_map<
3270 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3271 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003272 if (ec)
3273 {
3274 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3275 messages::internalError(aResp->res);
3276 return;
3277 }
3278
3279 // skip the empty postcode boots
3280 if (postcode.empty())
3281 {
3282 return;
3283 }
3284
3285 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3286
3287 aResp->res.jsonValue["Members@odata.count"] =
3288 aResp->res.jsonValue["Members"].size();
3289 },
Jonathan Doman15124762021-01-07 17:54:17 -08003290 "xyz.openbmc_project.State.Boot.PostCode0",
3291 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003292 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3293 bootIndex);
3294}
3295
zhanghch058d1b46d2021-04-01 11:18:24 +08003296static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003297 const uint16_t bootIndex,
3298 const uint16_t bootCount,
3299 const uint64_t entryCount, const uint64_t skip,
3300 const uint64_t top)
3301{
3302 crow::connections::systemBus->async_method_call(
3303 [aResp, bootIndex, bootCount, entryCount, skip,
3304 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303305 const boost::container::flat_map<
3306 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3307 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003308 if (ec)
3309 {
3310 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3311 messages::internalError(aResp->res);
3312 return;
3313 }
3314
3315 uint64_t endCount = entryCount;
3316 if (!postcode.empty())
3317 {
3318 endCount = entryCount + postcode.size();
3319
3320 if ((skip < endCount) && ((top + skip) > entryCount))
3321 {
3322 uint64_t thisBootSkip =
3323 std::max(skip, entryCount) - entryCount;
3324 uint64_t thisBootTop =
3325 std::min(top + skip, endCount) - entryCount;
3326
3327 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3328 thisBootSkip, thisBootTop);
3329 }
3330 aResp->res.jsonValue["Members@odata.count"] = endCount;
3331 }
3332
3333 // continue to previous bootIndex
3334 if (bootIndex < bootCount)
3335 {
3336 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3337 bootCount, endCount, skip, top);
3338 }
3339 else
3340 {
3341 aResp->res.jsonValue["Members@odata.nextLink"] =
George Liu0fda0f12021-11-16 10:06:17 +08003342 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
ZhikuiRena3316fc2020-01-29 14:58:08 -08003343 std::to_string(skip + top);
3344 }
3345 },
Jonathan Doman15124762021-01-07 17:54:17 -08003346 "xyz.openbmc_project.State.Boot.PostCode0",
3347 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003348 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3349 bootIndex);
3350}
3351
zhanghch058d1b46d2021-04-01 11:18:24 +08003352static void
3353 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3354 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003355{
3356 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003357 sdbusplus::asio::getProperty<uint16_t>(
3358 *crow::connections::systemBus,
3359 "xyz.openbmc_project.State.Boot.PostCode0",
3360 "/xyz/openbmc_project/State/Boot/PostCode0",
3361 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3362 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3363 const uint16_t bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003364 if (ec)
3365 {
3366 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3367 messages::internalError(aResp->res);
3368 return;
3369 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003370 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3371 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003372}
3373
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003374inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003375{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003376 BMCWEB_ROUTE(app,
3377 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003378 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003379 .methods(boost::beast::http::verb::get)(
3380 [](const crow::Request& req,
3381 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3382 asyncResp->res.jsonValue["@odata.type"] =
3383 "#LogEntryCollection.LogEntryCollection";
3384 asyncResp->res.jsonValue["@odata.id"] =
3385 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3386 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3387 asyncResp->res.jsonValue["Description"] =
3388 "Collection of POST Code Log Entries";
3389 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3390 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003391
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003392 uint64_t skip = 0;
3393 uint64_t top = maxEntriesPerPage; // Show max entries by default
3394 if (!getSkipParam(asyncResp, req, skip))
3395 {
3396 return;
3397 }
3398 if (!getTopParam(asyncResp, req, top))
3399 {
3400 return;
3401 }
3402 getCurrentBootNumber(asyncResp, skip, top);
3403 });
3404}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003405
George Liu647b3cd2021-07-05 12:43:56 +08003406/**
3407 * @brief Parse post code ID and get the current value and index value
3408 * eg: postCodeID=B1-2, currentValue=1, index=2
3409 *
3410 * @param[in] postCodeID Post Code ID
3411 * @param[out] currentValue Current value
3412 * @param[out] index Index value
3413 *
3414 * @return bool true if the parsing is successful, false the parsing fails
3415 */
3416inline static bool parsePostCode(const std::string& postCodeID,
3417 uint64_t& currentValue, uint16_t& index)
3418{
3419 std::vector<std::string> split;
3420 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3421 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3422 {
3423 return false;
3424 }
3425
Ed Tanousca45aa32022-01-07 09:28:45 -08003426 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003427 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003428 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003429 const char* end = split[0].data() + split[0].size();
3430 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3431
3432 if (ptrIndex != end || ecIndex != std::errc())
3433 {
3434 return false;
3435 }
3436
3437 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003438
3439 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003440 end = split[1].data() + split[1].size();
3441 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3442 if (ptrValue != end || ecValue != std::errc())
3443 {
3444 return false;
3445 }
3446
3447 return true;
3448}
3449
3450inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3451{
George Liu0fda0f12021-11-16 10:06:17 +08003452 BMCWEB_ROUTE(
3453 app,
3454 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003455 .privileges(redfish::privileges::getLogEntry)
3456 .methods(boost::beast::http::verb::get)(
3457 [](const crow::Request& req,
3458 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3459 const std::string& postCodeID) {
3460 if (!http_helpers::isOctetAccepted(
3461 req.getHeaderValue("Accept")))
3462 {
3463 asyncResp->res.result(
3464 boost::beast::http::status::bad_request);
3465 return;
3466 }
3467
3468 uint64_t currentValue = 0;
3469 uint16_t index = 0;
3470 if (!parsePostCode(postCodeID, currentValue, index))
3471 {
3472 messages::resourceNotFound(asyncResp->res, "LogEntry",
3473 postCodeID);
3474 return;
3475 }
3476
3477 crow::connections::systemBus->async_method_call(
3478 [asyncResp, postCodeID, currentValue](
3479 const boost::system::error_code ec,
3480 const std::vector<std::tuple<
3481 uint64_t, std::vector<uint8_t>>>& postcodes) {
3482 if (ec.value() == EBADR)
3483 {
3484 messages::resourceNotFound(asyncResp->res,
3485 "LogEntry", postCodeID);
3486 return;
3487 }
3488 if (ec)
3489 {
3490 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3491 messages::internalError(asyncResp->res);
3492 return;
3493 }
3494
3495 size_t value = static_cast<size_t>(currentValue) - 1;
3496 if (value == std::string::npos ||
3497 postcodes.size() < currentValue)
3498 {
3499 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3500 messages::resourceNotFound(asyncResp->res,
3501 "LogEntry", postCodeID);
3502 return;
3503 }
3504
Ed Tanous46ff87b2022-01-07 09:25:51 -08003505 auto& [tID, c] = postcodes[value];
3506 if (c.empty())
George Liu647b3cd2021-07-05 12:43:56 +08003507 {
3508 BMCWEB_LOG_INFO << "No found post code data";
3509 messages::resourceNotFound(asyncResp->res,
3510 "LogEntry", postCodeID);
3511 return;
3512 }
Ed Tanous46ff87b2022-01-07 09:25:51 -08003513 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3514 const char* d = reinterpret_cast<const char*>(c.data());
3515 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003516
3517 asyncResp->res.addHeader("Content-Type",
3518 "application/octet-stream");
3519 asyncResp->res.addHeader("Content-Transfer-Encoding",
3520 "Base64");
3521 asyncResp->res.body() =
3522 crow::utility::base64encode(strData);
3523 },
3524 "xyz.openbmc_project.State.Boot.PostCode0",
3525 "/xyz/openbmc_project/State/Boot/PostCode0",
3526 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3527 index);
3528 });
3529}
3530
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003531inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003532{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003533 BMCWEB_ROUTE(
3534 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003535 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003536 .methods(boost::beast::http::verb::get)(
3537 [](const crow::Request&,
3538 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3539 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003540 uint16_t bootIndex = 0;
3541 uint64_t codeIndex = 0;
3542 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003543 {
3544 // Requested ID was not found
3545 messages::resourceMissingAtURI(asyncResp->res, targetID);
3546 return;
3547 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003548 if (bootIndex == 0 || codeIndex == 0)
3549 {
3550 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3551 << targetID;
3552 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003553
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003554 asyncResp->res.jsonValue["@odata.type"] =
3555 "#LogEntry.v1_4_0.LogEntry";
3556 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08003557 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003558 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3559 asyncResp->res.jsonValue["Description"] =
3560 "Collection of POST Code Log Entries";
3561 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3562 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003563
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003564 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3565 });
3566}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003567
Ed Tanous1da66f72018-07-27 16:13:37 -07003568} // namespace redfish