blob: e20a2baacf3e66ca1b6f60443f00469fab37e15d [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;
Ed Tanousc419c752022-01-26 12:19:54 -08001431 const uint64_t* timestamp = nullptr;
1432 const uint64_t* updateTimestamp = nullptr;
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 {
Ed Tanousc419c752022-01-26 12:19:54 -08001451 timestamp = std::get_if<uint64_t>(
1452 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001453 }
1454 else if (propertyMap.first ==
1455 "UpdateTimestamp")
1456 {
Ed Tanousc419c752022-01-26 12:19:54 -08001457 updateTimestamp = std::get_if<uint64_t>(
1458 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001459 }
1460 else if (propertyMap.first == "Severity")
1461 {
1462 severity = std::get_if<std::string>(
1463 &propertyMap.second);
1464 }
1465 else if (propertyMap.first == "Message")
1466 {
1467 message = std::get_if<std::string>(
1468 &propertyMap.second);
1469 }
1470 else if (propertyMap.first == "Resolved")
1471 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001472 const bool* resolveptr =
1473 std::get_if<bool>(
1474 &propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001475 if (resolveptr == nullptr)
1476 {
1477 messages::internalError(
1478 asyncResp->res);
1479 return;
1480 }
1481 resolved = *resolveptr;
1482 }
1483 }
1484 if (id == nullptr || message == nullptr ||
1485 severity == nullptr)
1486 {
1487 messages::internalError(asyncResp->res);
1488 return;
1489 }
1490 }
1491 else if (interfaceMap.first ==
1492 "xyz.openbmc_project.Common.FilePath")
1493 {
1494 for (auto& propertyMap : interfaceMap.second)
1495 {
1496 if (propertyMap.first == "Path")
1497 {
1498 filePath = std::get_if<std::string>(
1499 &propertyMap.second);
1500 }
1501 }
1502 }
1503 }
1504 // Object path without the
1505 // xyz.openbmc_project.Logging.Entry interface, ignore
1506 // and continue.
1507 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001508 severity == nullptr || timestamp == nullptr ||
1509 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001510 {
1511 continue;
1512 }
1513 entriesArray.push_back({});
1514 nlohmann::json& thisEntry = entriesArray.back();
1515 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1516 thisEntry["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001517 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001518 std::to_string(*id);
1519 thisEntry["Name"] = "System Event Log Entry";
1520 thisEntry["Id"] = std::to_string(*id);
1521 thisEntry["Message"] = *message;
1522 thisEntry["Resolved"] = resolved;
1523 thisEntry["EntryType"] = "Event";
1524 thisEntry["Severity"] =
1525 translateSeverityDbusToRedfish(*severity);
1526 thisEntry["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001527 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001528 thisEntry["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001529 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001530 if (filePath != nullptr)
1531 {
1532 thisEntry["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001533 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001534 std::to_string(*id) + "/attachment";
1535 }
1536 }
1537 std::sort(entriesArray.begin(), entriesArray.end(),
1538 [](const nlohmann::json& left,
1539 const nlohmann::json& right) {
1540 return (left["Id"] <= right["Id"]);
1541 });
1542 asyncResp->res.jsonValue["Members@odata.count"] =
1543 entriesArray.size();
Xiaochao Ma75710de2021-01-21 17:56:02 +08001544 },
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001545 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1546 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1547 });
1548}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001549
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001550inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001551{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001552 BMCWEB_ROUTE(
1553 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001554 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001555 .methods(boost::beast::http::verb::get)(
1556 [](const crow::Request&,
1557 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1558 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001559
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001560 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001561 std::string entryID = param;
1562 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001563
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001564 // DBus implementation of EventLog/Entries
1565 // Make call to Logging Service to find all log entry objects
1566 crow::connections::systemBus->async_method_call(
1567 [asyncResp, entryID](const boost::system::error_code ec,
Ed Tanous914e2d52022-01-07 11:38:34 -08001568 const GetManagedPropertyType& resp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001569 if (ec.value() == EBADR)
1570 {
1571 messages::resourceNotFound(
1572 asyncResp->res, "EventLogEntry", entryID);
1573 return;
1574 }
1575 if (ec)
1576 {
George Liu0fda0f12021-11-16 10:06:17 +08001577 BMCWEB_LOG_ERROR
1578 << "EventLogEntry (DBus) resp_handler got error "
1579 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001580 messages::internalError(asyncResp->res);
1581 return;
1582 }
Ed Tanous914e2d52022-01-07 11:38:34 -08001583 const uint32_t* id = nullptr;
Ed Tanousc419c752022-01-26 12:19:54 -08001584 const uint64_t* timestamp = nullptr;
1585 const uint64_t* updateTimestamp = nullptr;
Ed Tanous914e2d52022-01-07 11:38:34 -08001586 const std::string* severity = nullptr;
1587 const std::string* message = nullptr;
1588 const std::string* filePath = nullptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001589 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001590
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001591 for (auto& propertyMap : resp)
1592 {
1593 if (propertyMap.first == "Id")
1594 {
1595 id = std::get_if<uint32_t>(&propertyMap.second);
1596 }
1597 else if (propertyMap.first == "Timestamp")
1598 {
Ed Tanousc419c752022-01-26 12:19:54 -08001599 timestamp =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001600 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001601 }
1602 else if (propertyMap.first == "UpdateTimestamp")
1603 {
Ed Tanousc419c752022-01-26 12:19:54 -08001604 updateTimestamp =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001605 std::get_if<uint64_t>(&propertyMap.second);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001606 }
1607 else if (propertyMap.first == "Severity")
1608 {
1609 severity = std::get_if<std::string>(
1610 &propertyMap.second);
1611 }
1612 else if (propertyMap.first == "Message")
1613 {
1614 message = std::get_if<std::string>(
1615 &propertyMap.second);
1616 }
1617 else if (propertyMap.first == "Resolved")
1618 {
Ed Tanous914e2d52022-01-07 11:38:34 -08001619 const bool* resolveptr =
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001620 std::get_if<bool>(&propertyMap.second);
1621 if (resolveptr == nullptr)
1622 {
1623 messages::internalError(asyncResp->res);
1624 return;
1625 }
1626 resolved = *resolveptr;
1627 }
1628 else if (propertyMap.first == "Path")
1629 {
1630 filePath = std::get_if<std::string>(
1631 &propertyMap.second);
1632 }
1633 }
1634 if (id == nullptr || message == nullptr ||
Ed Tanousc419c752022-01-26 12:19:54 -08001635 severity == nullptr || timestamp == nullptr ||
1636 updateTimestamp == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001637 {
1638 messages::internalError(asyncResp->res);
1639 return;
1640 }
1641 asyncResp->res.jsonValue["@odata.type"] =
1642 "#LogEntry.v1_8_0.LogEntry";
1643 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08001644 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001645 std::to_string(*id);
1646 asyncResp->res.jsonValue["Name"] =
1647 "System Event Log Entry";
1648 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1649 asyncResp->res.jsonValue["Message"] = *message;
1650 asyncResp->res.jsonValue["Resolved"] = resolved;
1651 asyncResp->res.jsonValue["EntryType"] = "Event";
1652 asyncResp->res.jsonValue["Severity"] =
1653 translateSeverityDbusToRedfish(*severity);
1654 asyncResp->res.jsonValue["Created"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001655 crow::utility::getDateTimeUintMs(*timestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001656 asyncResp->res.jsonValue["Modified"] =
Ed Tanousc419c752022-01-26 12:19:54 -08001657 crow::utility::getDateTimeUintMs(*updateTimestamp);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001658 if (filePath != nullptr)
1659 {
1660 asyncResp->res.jsonValue["AdditionalDataURI"] =
George Liu0fda0f12021-11-16 10:06:17 +08001661 "/redfish/v1/Systems/system/LogServices/EventLog/attachment/" +
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001662 std::to_string(*id);
1663 }
1664 },
1665 "xyz.openbmc_project.Logging",
1666 "/xyz/openbmc_project/logging/entry/" + entryID,
1667 "org.freedesktop.DBus.Properties", "GetAll", "");
1668 });
1669
1670 BMCWEB_ROUTE(
1671 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001672 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001673 .methods(boost::beast::http::verb::patch)(
1674 [](const crow::Request& req,
1675 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1676 const std::string& entryId) {
1677 std::optional<bool> resolved;
1678
1679 if (!json_util::readJson(req, asyncResp->res, "Resolved",
1680 resolved))
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001681 {
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001682 return;
1683 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001684 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001685
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001686 crow::connections::systemBus->async_method_call(
Ed Tanous4f48d5f2021-06-21 08:27:45 -07001687 [asyncResp, entryId](const boost::system::error_code ec) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001688 if (ec)
1689 {
1690 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1691 messages::internalError(asyncResp->res);
1692 return;
1693 }
1694 },
1695 "xyz.openbmc_project.Logging",
1696 "/xyz/openbmc_project/logging/entry/" + entryId,
1697 "org.freedesktop.DBus.Properties", "Set",
1698 "xyz.openbmc_project.Logging.Entry", "Resolved",
Ed Tanous168e20c2021-12-13 14:39:53 -08001699 dbus::utility::DbusVariantType(*resolved));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001700 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001701
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001702 BMCWEB_ROUTE(
1703 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001704 .privileges(redfish::privileges::deleteLogEntry)
1705
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001706 .methods(boost::beast::http::verb::delete_)(
1707 [](const crow::Request&,
1708 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1709 const std::string& param)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001710
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001711 {
1712 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001713
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001714 std::string entryID = param;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001715
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001716 dbus::utility::escapePathForDbus(entryID);
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001717
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001718 // Process response from Logging service.
1719 auto respHandler = [asyncResp, entryID](
1720 const boost::system::error_code ec) {
1721 BMCWEB_LOG_DEBUG
1722 << "EventLogEntry (DBus) doDelete callback: Done";
1723 if (ec)
1724 {
1725 if (ec.value() == EBADR)
1726 {
1727 messages::resourceNotFound(asyncResp->res,
1728 "LogEntry", entryID);
1729 return;
1730 }
1731 // TODO Handle for specific error code
George Liu0fda0f12021-11-16 10:06:17 +08001732 BMCWEB_LOG_ERROR
1733 << "EventLogEntry (DBus) doDelete respHandler got error "
1734 << ec;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001735 asyncResp->res.result(
1736 boost::beast::http::status::internal_server_error);
1737 return;
1738 }
1739
1740 asyncResp->res.result(boost::beast::http::status::ok);
1741 };
1742
1743 // Make call to Logging service to request Delete Log
1744 crow::connections::systemBus->async_method_call(
1745 respHandler, "xyz.openbmc_project.Logging",
1746 "/xyz/openbmc_project/logging/entry/" + entryID,
1747 "xyz.openbmc_project.Object.Delete", "Delete");
1748 });
1749}
1750
1751inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001752{
George Liu0fda0f12021-11-16 10:06:17 +08001753 BMCWEB_ROUTE(
1754 app,
1755 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001756 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001757 .methods(boost::beast::http::verb::get)(
1758 [](const crow::Request& req,
1759 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1760 const std::string& param)
Ed Tanous1da66f72018-07-27 16:13:37 -07001761
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001762 {
George Liu647b3cd2021-07-05 12:43:56 +08001763 if (!http_helpers::isOctetAccepted(
1764 req.getHeaderValue("Accept")))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001765 {
1766 asyncResp->res.result(
1767 boost::beast::http::status::bad_request);
1768 return;
1769 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001770
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001771 std::string entryID = param;
1772 dbus::utility::escapePathForDbus(entryID);
1773
1774 crow::connections::systemBus->async_method_call(
1775 [asyncResp,
1776 entryID](const boost::system::error_code ec,
1777 const sdbusplus::message::unix_fd& unixfd) {
1778 if (ec.value() == EBADR)
1779 {
1780 messages::resourceNotFound(
1781 asyncResp->res, "EventLogAttachment", entryID);
1782 return;
1783 }
1784 if (ec)
1785 {
1786 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1787 messages::internalError(asyncResp->res);
1788 return;
1789 }
1790
1791 int fd = -1;
1792 fd = dup(unixfd);
1793 if (fd == -1)
1794 {
1795 messages::internalError(asyncResp->res);
1796 return;
1797 }
1798
1799 long long int size = lseek(fd, 0, SEEK_END);
1800 if (size == -1)
1801 {
1802 messages::internalError(asyncResp->res);
1803 return;
1804 }
1805
1806 // Arbitrary max size of 64kb
1807 constexpr int maxFileSize = 65536;
1808 if (size > maxFileSize)
1809 {
1810 BMCWEB_LOG_ERROR
1811 << "File size exceeds maximum allowed size of "
1812 << maxFileSize;
1813 messages::internalError(asyncResp->res);
1814 return;
1815 }
1816 std::vector<char> data(static_cast<size_t>(size));
1817 long long int rc = lseek(fd, 0, SEEK_SET);
1818 if (rc == -1)
1819 {
1820 messages::internalError(asyncResp->res);
1821 return;
1822 }
1823 rc = read(fd, data.data(), data.size());
1824 if ((rc == -1) || (rc != size))
1825 {
1826 messages::internalError(asyncResp->res);
1827 return;
1828 }
1829 close(fd);
1830
1831 std::string_view strData(data.data(), data.size());
1832 std::string output =
1833 crow::utility::base64encode(strData);
1834
1835 asyncResp->res.addHeader("Content-Type",
1836 "application/octet-stream");
1837 asyncResp->res.addHeader("Content-Transfer-Encoding",
1838 "Base64");
1839 asyncResp->res.body() = std::move(output);
1840 },
1841 "xyz.openbmc_project.Logging",
1842 "/xyz/openbmc_project/logging/entry/" + entryID,
1843 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1844 });
1845}
1846
Spencer Kub7028eb2021-10-26 15:27:35 +08001847constexpr const char* hostLoggerFolderPath = "/var/log/console";
1848
1849inline bool
1850 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1851 std::vector<std::filesystem::path>& hostLoggerFiles)
1852{
1853 std::error_code ec;
1854 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1855 if (ec)
1856 {
1857 BMCWEB_LOG_ERROR << ec.message();
1858 return false;
1859 }
1860 for (const std::filesystem::directory_entry& it : logPath)
1861 {
1862 std::string filename = it.path().filename();
1863 // Prefix of each log files is "log". Find the file and save the
1864 // path
1865 if (boost::starts_with(filename, "log"))
1866 {
1867 hostLoggerFiles.emplace_back(it.path());
1868 }
1869 }
1870 // As the log files rotate, they are appended with a ".#" that is higher for
1871 // the older logs. Since we start from oldest logs, sort the name in
1872 // descending order.
1873 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1874 AlphanumLess<std::string>());
1875
1876 return true;
1877}
1878
1879inline bool
1880 getHostLoggerEntries(std::vector<std::filesystem::path>& hostLoggerFiles,
1881 uint64_t& skip, uint64_t& top,
1882 std::vector<std::string>& logEntries, size_t& logCount)
1883{
1884 GzFileReader logFile;
1885
1886 // Go though all log files and expose host logs.
1887 for (const std::filesystem::path& it : hostLoggerFiles)
1888 {
1889 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1890 {
1891 BMCWEB_LOG_ERROR << "fail to expose host logs";
1892 return false;
1893 }
1894 }
1895 // Get lastMessage from constructor by getter
1896 std::string lastMessage = logFile.getLastMessage();
1897 if (!lastMessage.empty())
1898 {
1899 logCount++;
1900 if (logCount > skip && logCount <= (skip + top))
1901 {
1902 logEntries.push_back(lastMessage);
1903 }
1904 }
1905 return true;
1906}
1907
1908inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1909 const std::string& msg,
1910 nlohmann::json& logEntryJson)
1911{
1912 // Fill in the log entry with the gathered data.
1913 logEntryJson = {
1914 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1915 {"@odata.id",
1916 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1917 logEntryID},
1918 {"Name", "Host Logger Entry"},
1919 {"Id", logEntryID},
1920 {"Message", msg},
1921 {"EntryType", "Oem"},
1922 {"Severity", "OK"},
1923 {"OemRecordFormat", "Host Logger Entry"}};
1924}
1925
1926inline void requestRoutesSystemHostLogger(App& app)
1927{
1928 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1929 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08001930 .methods(
1931 boost::beast::http::verb::
1932 get)([](const crow::Request&,
1933 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1934 asyncResp->res.jsonValue["@odata.id"] =
1935 "/redfish/v1/Systems/system/LogServices/HostLogger";
1936 asyncResp->res.jsonValue["@odata.type"] =
1937 "#LogService.v1_1_0.LogService";
1938 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1939 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1940 asyncResp->res.jsonValue["Id"] = "HostLogger";
1941 asyncResp->res.jsonValue["Entries"] = {
1942 {"@odata.id",
1943 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries"}};
1944 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001945}
1946
1947inline void requestRoutesSystemHostLoggerCollection(App& app)
1948{
1949 BMCWEB_ROUTE(app,
1950 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1951 .privileges(redfish::privileges::getLogEntry)
George Liu0fda0f12021-11-16 10:06:17 +08001952 .methods(
1953 boost::beast::http::verb::
1954 get)([](const crow::Request& req,
1955 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1956 uint64_t skip = 0;
1957 uint64_t top = maxEntriesPerPage; // Show max 1000 entries by
1958 // default, allow range 1 to
1959 // 1000 entries per page.
1960 if (!getSkipParam(asyncResp, req, skip))
1961 {
1962 return;
1963 }
1964 if (!getTopParam(asyncResp, req, top))
1965 {
1966 return;
1967 }
1968 asyncResp->res.jsonValue["@odata.id"] =
1969 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1970 asyncResp->res.jsonValue["@odata.type"] =
1971 "#LogEntryCollection.LogEntryCollection";
1972 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1973 asyncResp->res.jsonValue["Description"] =
1974 "Collection of HostLogger Entries";
1975 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1976 logEntryArray = nlohmann::json::array();
1977 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001978
George Liu0fda0f12021-11-16 10:06:17 +08001979 std::vector<std::filesystem::path> hostLoggerFiles;
1980 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1981 {
1982 BMCWEB_LOG_ERROR << "fail to get host log file path";
1983 return;
1984 }
1985
1986 size_t logCount = 0;
1987 // This vector only store the entries we want to expose that
1988 // control by skip and top.
1989 std::vector<std::string> logEntries;
1990 if (!getHostLoggerEntries(hostLoggerFiles, skip, top, logEntries,
1991 logCount))
1992 {
1993 messages::internalError(asyncResp->res);
1994 return;
1995 }
1996 // If vector is empty, that means skip value larger than total
1997 // log count
Ed Tanous26f69762022-01-25 09:49:11 -08001998 if (logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08001999 {
2000 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
2001 return;
2002 }
Ed Tanous26f69762022-01-25 09:49:11 -08002003 if (!logEntries.empty())
George Liu0fda0f12021-11-16 10:06:17 +08002004 {
2005 for (size_t i = 0; i < logEntries.size(); i++)
Spencer Kub7028eb2021-10-26 15:27:35 +08002006 {
George Liu0fda0f12021-11-16 10:06:17 +08002007 logEntryArray.push_back({});
2008 nlohmann::json& hostLogEntry = logEntryArray.back();
2009 fillHostLoggerEntryJson(std::to_string(skip + i),
2010 logEntries[i], hostLogEntry);
Spencer Kub7028eb2021-10-26 15:27:35 +08002011 }
2012
George Liu0fda0f12021-11-16 10:06:17 +08002013 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
2014 if (skip + top < logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08002015 {
George Liu0fda0f12021-11-16 10:06:17 +08002016 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2017 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
2018 std::to_string(skip + top);
Spencer Kub7028eb2021-10-26 15:27:35 +08002019 }
George Liu0fda0f12021-11-16 10:06:17 +08002020 }
2021 });
Spencer Kub7028eb2021-10-26 15:27:35 +08002022}
2023
2024inline void requestRoutesSystemHostLoggerLogEntry(App& app)
2025{
2026 BMCWEB_ROUTE(
2027 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
2028 .privileges(redfish::privileges::getLogEntry)
2029 .methods(boost::beast::http::verb::get)(
2030 [](const crow::Request&,
2031 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2032 const std::string& param) {
2033 const std::string& targetID = param;
2034
2035 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08002036
2037 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
2038 const char* end = targetID.data() + targetID.size();
2039
2040 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
Spencer Kub7028eb2021-10-26 15:27:35 +08002041 if (ec == std::errc::invalid_argument)
2042 {
2043 messages::resourceMissingAtURI(asyncResp->res, targetID);
2044 return;
2045 }
2046 if (ec == std::errc::result_out_of_range)
2047 {
2048 messages::resourceMissingAtURI(asyncResp->res, targetID);
2049 return;
2050 }
2051
2052 std::vector<std::filesystem::path> hostLoggerFiles;
2053 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
2054 {
2055 BMCWEB_LOG_ERROR << "fail to get host log file path";
2056 return;
2057 }
2058
2059 size_t logCount = 0;
2060 uint64_t top = 1;
2061 std::vector<std::string> logEntries;
2062 // We can get specific entry by skip and top. For example, if we
2063 // want to get nth entry, we can set skip = n-1 and top = 1 to
2064 // get that entry
2065 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top,
2066 logEntries, logCount))
2067 {
2068 messages::internalError(asyncResp->res);
2069 return;
2070 }
2071
2072 if (!logEntries.empty())
2073 {
2074 fillHostLoggerEntryJson(targetID, logEntries[0],
2075 asyncResp->res.jsonValue);
2076 return;
2077 }
2078
2079 // Requested ID was not found
2080 messages::resourceMissingAtURI(asyncResp->res, targetID);
2081 });
2082}
2083
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002084inline void requestRoutesBMCLogServiceCollection(App& app)
2085{
2086 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002087 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002088 .methods(boost::beast::http::verb::get)(
2089 [](const crow::Request&,
2090 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2091 // Collections don't include the static data added by SubRoute
2092 // because it has a duplicate entry for members
2093 asyncResp->res.jsonValue["@odata.type"] =
2094 "#LogServiceCollection.LogServiceCollection";
2095 asyncResp->res.jsonValue["@odata.id"] =
2096 "/redfish/v1/Managers/bmc/LogServices";
2097 asyncResp->res.jsonValue["Name"] =
2098 "Open BMC Log Services Collection";
2099 asyncResp->res.jsonValue["Description"] =
2100 "Collection of LogServices for this Manager";
2101 nlohmann::json& logServiceArray =
2102 asyncResp->res.jsonValue["Members"];
2103 logServiceArray = nlohmann::json::array();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002104#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002105 logServiceArray.push_back(
2106 {{"@odata.id",
2107 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -05002108#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002109#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002110 logServiceArray.push_back(
2111 {{"@odata.id",
2112 "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002113#endif
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002114 asyncResp->res.jsonValue["Members@odata.count"] =
2115 logServiceArray.size();
2116 });
2117}
Ed Tanous1da66f72018-07-27 16:13:37 -07002118
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002119inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002120{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002121 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002122 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002123 .methods(boost::beast::http::verb::get)(
2124 [](const crow::Request&,
2125 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002126
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002127 {
2128 asyncResp->res.jsonValue["@odata.type"] =
2129 "#LogService.v1_1_0.LogService";
2130 asyncResp->res.jsonValue["@odata.id"] =
2131 "/redfish/v1/Managers/bmc/LogServices/Journal";
2132 asyncResp->res.jsonValue["Name"] =
2133 "Open BMC Journal Log Service";
2134 asyncResp->res.jsonValue["Description"] =
2135 "BMC Journal Log Service";
2136 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2137 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302138
2139 std::pair<std::string, std::string> redfishDateTimeOffset =
2140 crow::utility::getDateTimeOffsetNow();
2141 asyncResp->res.jsonValue["DateTime"] =
2142 redfishDateTimeOffset.first;
2143 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2144 redfishDateTimeOffset.second;
2145
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002146 asyncResp->res.jsonValue["Entries"] = {
2147 {"@odata.id",
2148 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
2149 });
2150}
Jason M. Billse1f26342018-07-18 12:12:00 -07002151
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002152static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2153 sd_journal* journal,
2154 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002155{
2156 // Get the Log Entry contents
2157 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002158
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002159 std::string message;
2160 std::string_view syslogID;
2161 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2162 if (ret < 0)
2163 {
2164 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2165 << strerror(-ret);
2166 }
2167 if (!syslogID.empty())
2168 {
2169 message += std::string(syslogID) + ": ";
2170 }
2171
Ed Tanous39e77502019-03-04 17:35:53 -08002172 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002173 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002174 if (ret < 0)
2175 {
2176 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2177 return 1;
2178 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002179 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002180
2181 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002182 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002183 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002184 if (ret < 0)
2185 {
2186 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002187 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002188
2189 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002190 std::string entryTimeStr;
2191 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002192 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002193 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002194 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002195
2196 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002197 bmcJournalLogEntryJson = {
George Liu647b3cd2021-07-05 12:43:56 +08002198 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002199 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2200 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07002201 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08002202 {"Id", bmcJournalLogEntryID},
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002203 {"Message", std::move(message)},
Jason M. Billse1f26342018-07-18 12:12:00 -07002204 {"EntryType", "Oem"},
Patrick Williams738c1e62021-02-22 17:14:25 -06002205 {"Severity", severity <= 2 ? "Critical"
2206 : severity <= 4 ? "Warning"
2207 : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07002208 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07002209 {"Created", std::move(entryTimeStr)}};
2210 return 0;
2211}
2212
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002213inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002214{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002215 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002216 .privileges(redfish::privileges::getLogEntryCollection)
George Liu0fda0f12021-11-16 10:06:17 +08002217 .methods(
2218 boost::beast::http::verb::
2219 get)([](const crow::Request& req,
2220 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2221 static constexpr const long maxEntriesPerPage = 1000;
2222 uint64_t skip = 0;
2223 uint64_t top = maxEntriesPerPage; // Show max entries by default
2224 if (!getSkipParam(asyncResp, req, skip))
2225 {
2226 return;
2227 }
2228 if (!getTopParam(asyncResp, req, top))
2229 {
2230 return;
2231 }
2232 // Collections don't include the static data added by SubRoute
2233 // because it has a duplicate entry for members
2234 asyncResp->res.jsonValue["@odata.type"] =
2235 "#LogEntryCollection.LogEntryCollection";
2236 asyncResp->res.jsonValue["@odata.id"] =
2237 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2238 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2239 asyncResp->res.jsonValue["Description"] =
2240 "Collection of BMC Journal Entries";
2241 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2242 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002243
George Liu0fda0f12021-11-16 10:06:17 +08002244 // Go through the journal and use the timestamp to create a
2245 // unique ID for each entry
2246 sd_journal* journalTmp = nullptr;
2247 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2248 if (ret < 0)
2249 {
2250 BMCWEB_LOG_ERROR << "failed to open journal: "
2251 << strerror(-ret);
2252 messages::internalError(asyncResp->res);
2253 return;
2254 }
2255 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2256 journalTmp, sd_journal_close);
2257 journalTmp = nullptr;
2258 uint64_t entryCount = 0;
2259 // Reset the unique ID on the first entry
2260 bool firstEntry = true;
2261 SD_JOURNAL_FOREACH(journal.get())
2262 {
2263 entryCount++;
2264 // Handle paging using skip (number of entries to skip from
2265 // the start) and top (number of entries to display)
2266 if (entryCount <= skip || entryCount > skip + top)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002267 {
George Liu0fda0f12021-11-16 10:06:17 +08002268 continue;
2269 }
2270
2271 std::string idStr;
2272 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2273 {
2274 continue;
2275 }
2276
2277 if (firstEntry)
2278 {
2279 firstEntry = false;
2280 }
2281
2282 logEntryArray.push_back({});
2283 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
2284 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2285 bmcJournalLogEntry) != 0)
2286 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002287 messages::internalError(asyncResp->res);
2288 return;
2289 }
George Liu0fda0f12021-11-16 10:06:17 +08002290 }
2291 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2292 if (skip + top < entryCount)
2293 {
2294 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2295 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2296 std::to_string(skip + top);
2297 }
2298 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002299}
Jason M. Billse1f26342018-07-18 12:12:00 -07002300
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002301inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002302{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002303 BMCWEB_ROUTE(app,
2304 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002305 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002306 .methods(boost::beast::http::verb::get)(
2307 [](const crow::Request&,
2308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2309 const std::string& entryID) {
2310 // Convert the unique ID back to a timestamp to find the entry
2311 uint64_t ts = 0;
2312 uint64_t index = 0;
2313 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2314 {
2315 return;
2316 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002317
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002318 sd_journal* journalTmp = nullptr;
2319 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2320 if (ret < 0)
2321 {
2322 BMCWEB_LOG_ERROR << "failed to open journal: "
2323 << strerror(-ret);
2324 messages::internalError(asyncResp->res);
2325 return;
2326 }
2327 std::unique_ptr<sd_journal, decltype(&sd_journal_close)>
2328 journal(journalTmp, sd_journal_close);
2329 journalTmp = nullptr;
2330 // Go to the timestamp in the log and move to the entry at the
2331 // index tracking the unique ID
2332 std::string idStr;
2333 bool firstEntry = true;
2334 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2335 if (ret < 0)
2336 {
2337 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2338 << strerror(-ret);
2339 messages::internalError(asyncResp->res);
2340 return;
2341 }
2342 for (uint64_t i = 0; i <= index; i++)
2343 {
2344 sd_journal_next(journal.get());
2345 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2346 {
2347 messages::internalError(asyncResp->res);
2348 return;
2349 }
2350 if (firstEntry)
2351 {
2352 firstEntry = false;
2353 }
2354 }
2355 // Confirm that the entry ID matches what was requested
2356 if (idStr != entryID)
2357 {
2358 messages::resourceMissingAtURI(asyncResp->res, entryID);
2359 return;
2360 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002361
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002362 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
2363 asyncResp->res.jsonValue) != 0)
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002364 {
2365 messages::internalError(asyncResp->res);
2366 return;
2367 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002368 });
2369}
2370
2371inline void requestRoutesBMCDumpService(App& app)
2372{
2373 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002374 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08002375 .methods(
2376 boost::beast::http::verb::
2377 get)([](const crow::Request&,
2378 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2379 asyncResp->res.jsonValue["@odata.id"] =
2380 "/redfish/v1/Managers/bmc/LogServices/Dump";
2381 asyncResp->res.jsonValue["@odata.type"] =
2382 "#LogService.v1_2_0.LogService";
2383 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2384 asyncResp->res.jsonValue["Description"] = "BMC Dump LogService";
2385 asyncResp->res.jsonValue["Id"] = "Dump";
2386 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302387
George Liu0fda0f12021-11-16 10:06:17 +08002388 std::pair<std::string, std::string> redfishDateTimeOffset =
2389 crow::utility::getDateTimeOffsetNow();
2390 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2391 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2392 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302393
George Liu0fda0f12021-11-16 10:06:17 +08002394 asyncResp->res.jsonValue["Entries"] = {
2395 {"@odata.id",
2396 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries"}};
2397 asyncResp->res.jsonValue["Actions"] = {
2398 {"#LogService.ClearLog",
2399 {{"target",
2400 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog"}}},
2401 {"#LogService.CollectDiagnosticData",
2402 {{"target",
2403 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
2404 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002405}
2406
2407inline void requestRoutesBMCDumpEntryCollection(App& app)
2408{
2409
2410 /**
2411 * Functions triggers appropriate requests on DBus
2412 */
2413 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002414 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002415 .methods(boost::beast::http::verb::get)(
2416 [](const crow::Request&,
2417 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2418 asyncResp->res.jsonValue["@odata.type"] =
2419 "#LogEntryCollection.LogEntryCollection";
2420 asyncResp->res.jsonValue["@odata.id"] =
2421 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries";
2422 asyncResp->res.jsonValue["Name"] = "BMC Dump Entries";
2423 asyncResp->res.jsonValue["Description"] =
2424 "Collection of BMC Dump Entries";
2425
2426 getDumpEntryCollection(asyncResp, "BMC");
2427 });
2428}
2429
2430inline void requestRoutesBMCDumpEntry(App& app)
2431{
2432 BMCWEB_ROUTE(app,
2433 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002434 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002435 .methods(boost::beast::http::verb::get)(
2436 [](const crow::Request&,
2437 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2438 const std::string& param) {
2439 getDumpEntryById(asyncResp, param, "BMC");
2440 });
2441 BMCWEB_ROUTE(app,
2442 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002443 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002444 .methods(boost::beast::http::verb::delete_)(
2445 [](const crow::Request&,
2446 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2447 const std::string& param) {
2448 deleteDumpEntry(asyncResp, param, "bmc");
2449 });
2450}
2451
2452inline void requestRoutesBMCDumpCreate(App& app)
2453{
2454
George Liu0fda0f12021-11-16 10:06:17 +08002455 BMCWEB_ROUTE(
2456 app,
2457 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002458 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002459 .methods(boost::beast::http::verb::post)(
2460 [](const crow::Request& req,
2461 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2462 createDump(asyncResp, req, "BMC");
2463 });
2464}
2465
2466inline void requestRoutesBMCDumpClear(App& app)
2467{
George Liu0fda0f12021-11-16 10:06:17 +08002468 BMCWEB_ROUTE(
2469 app,
2470 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002471 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002472 .methods(boost::beast::http::verb::post)(
2473 [](const crow::Request&,
2474 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2475 clearDump(asyncResp, "BMC");
2476 });
2477}
2478
2479inline void requestRoutesSystemDumpService(App& app)
2480{
2481 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002482 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002483 .methods(boost::beast::http::verb::get)(
2484 [](const crow::Request&,
2485 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2486
2487 {
2488 asyncResp->res.jsonValue["@odata.id"] =
2489 "/redfish/v1/Systems/system/LogServices/Dump";
2490 asyncResp->res.jsonValue["@odata.type"] =
2491 "#LogService.v1_2_0.LogService";
2492 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2493 asyncResp->res.jsonValue["Description"] =
2494 "System Dump LogService";
2495 asyncResp->res.jsonValue["Id"] = "Dump";
2496 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302497
2498 std::pair<std::string, std::string> redfishDateTimeOffset =
2499 crow::utility::getDateTimeOffsetNow();
2500 asyncResp->res.jsonValue["DateTime"] =
2501 redfishDateTimeOffset.first;
2502 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2503 redfishDateTimeOffset.second;
2504
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002505 asyncResp->res.jsonValue["Entries"] = {
2506 {"@odata.id",
2507 "/redfish/v1/Systems/system/LogServices/Dump/Entries"}};
2508 asyncResp->res.jsonValue["Actions"] = {
2509 {"#LogService.ClearLog",
2510 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002511 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002512 {"#LogService.CollectDiagnosticData",
2513 {{"target",
George Liu0fda0f12021-11-16 10:06:17 +08002514 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002515 });
2516}
2517
2518inline void requestRoutesSystemDumpEntryCollection(App& app)
2519{
2520
2521 /**
2522 * Functions triggers appropriate requests on DBus
2523 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002524 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002525 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002526 .methods(boost::beast::http::verb::get)(
2527 [](const crow::Request&,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002528 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002529 asyncResp->res.jsonValue["@odata.type"] =
2530 "#LogEntryCollection.LogEntryCollection";
2531 asyncResp->res.jsonValue["@odata.id"] =
2532 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2533 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
2534 asyncResp->res.jsonValue["Description"] =
2535 "Collection of System Dump Entries";
2536
2537 getDumpEntryCollection(asyncResp, "System");
2538 });
2539}
2540
2541inline void requestRoutesSystemDumpEntry(App& app)
2542{
2543 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002544 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002545 .privileges(redfish::privileges::getLogEntry)
2546
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002547 .methods(boost::beast::http::verb::get)(
2548 [](const crow::Request&,
2549 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2550 const std::string& param) {
2551 getDumpEntryById(asyncResp, param, "System");
2552 });
2553
2554 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002555 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002556 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002557 .methods(boost::beast::http::verb::delete_)(
2558 [](const crow::Request&,
2559 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2560 const std::string& param) {
2561 deleteDumpEntry(asyncResp, param, "system");
2562 });
2563}
2564
2565inline void requestRoutesSystemDumpCreate(App& app)
2566{
George Liu0fda0f12021-11-16 10:06:17 +08002567 BMCWEB_ROUTE(
2568 app,
2569 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002570 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002571 .methods(boost::beast::http::verb::post)(
2572 [](const crow::Request& req,
2573 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2574
2575 { createDump(asyncResp, req, "System"); });
2576}
2577
2578inline void requestRoutesSystemDumpClear(App& app)
2579{
George Liu0fda0f12021-11-16 10:06:17 +08002580 BMCWEB_ROUTE(
2581 app,
2582 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002583 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002584 .methods(boost::beast::http::verb::post)(
2585 [](const crow::Request&,
2586 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2587
2588 { clearDump(asyncResp, "System"); });
2589}
2590
2591inline void requestRoutesCrashdumpService(App& app)
2592{
2593 // Note: Deviated from redfish privilege registry for GET & HEAD
2594 // method for security reasons.
2595 /**
2596 * Functions triggers appropriate requests on DBus
2597 */
2598 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002599 // This is incorrect, should be:
2600 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002601 .privileges({{"ConfigureManager"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002602 .methods(
2603 boost::beast::http::verb::
2604 get)([](const crow::Request&,
2605 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2606 // Copy over the static data to include the entries added by
2607 // SubRoute
2608 asyncResp->res.jsonValue["@odata.id"] =
2609 "/redfish/v1/Systems/system/LogServices/Crashdump";
2610 asyncResp->res.jsonValue["@odata.type"] =
2611 "#LogService.v1_2_0.LogService";
2612 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2613 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2614 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2615 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2616 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302617
2618 std::pair<std::string, std::string> redfishDateTimeOffset =
2619 crow::utility::getDateTimeOffsetNow();
2620 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2621 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2622 redfishDateTimeOffset.second;
2623
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002624 asyncResp->res.jsonValue["Entries"] = {
2625 {"@odata.id",
2626 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
2627 asyncResp->res.jsonValue["Actions"] = {
2628 {"#LogService.ClearLog",
George Liu0fda0f12021-11-16 10:06:17 +08002629 {{"target",
2630 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog"}}},
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002631 {"#LogService.CollectDiagnosticData",
George Liu0fda0f12021-11-16 10:06:17 +08002632 {{"target",
2633 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData"}}}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002634 });
2635}
2636
2637void inline requestRoutesCrashdumpClear(App& app)
2638{
George Liu0fda0f12021-11-16 10:06:17 +08002639 BMCWEB_ROUTE(
2640 app,
2641 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002642 // This is incorrect, should be:
2643 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002644 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002645 .methods(boost::beast::http::verb::post)(
2646 [](const crow::Request&,
2647 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2648 crow::connections::systemBus->async_method_call(
2649 [asyncResp](const boost::system::error_code ec,
2650 const std::string&) {
2651 if (ec)
2652 {
2653 messages::internalError(asyncResp->res);
2654 return;
2655 }
2656 messages::success(asyncResp->res);
2657 },
2658 crashdumpObject, crashdumpPath, deleteAllInterface,
2659 "DeleteAll");
2660 });
2661}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002662
zhanghch058d1b46d2021-04-01 11:18:24 +08002663static void
2664 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2665 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002666{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002667 auto getStoredLogCallback =
2668 [asyncResp, logID, &logEntryJson](
2669 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002670 const std::vector<
2671 std::pair<std::string, dbus::utility::DbusVariantType>>&
2672 params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002673 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002674 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002675 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2676 if (ec.value() ==
2677 boost::system::linux_error::bad_request_descriptor)
2678 {
2679 messages::resourceNotFound(asyncResp->res, "LogEntry",
2680 logID);
2681 }
2682 else
2683 {
2684 messages::internalError(asyncResp->res);
2685 }
2686 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002687 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002688
Johnathan Mantey043a0532020-03-10 17:15:28 -07002689 std::string timestamp{};
2690 std::string filename{};
2691 std::string logfile{};
Ed Tanous2c70f802020-09-28 14:29:23 -07002692 parseCrashdumpParameters(params, filename, timestamp, logfile);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002693
2694 if (filename.empty() || timestamp.empty())
2695 {
2696 messages::resourceMissingAtURI(asyncResp->res, logID);
2697 return;
2698 }
2699
2700 std::string crashdumpURI =
2701 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2702 logID + "/" + filename;
Ed Tanousd0dbeef2021-07-01 08:46:46 -07002703 logEntryJson = {{"@odata.type", "#LogEntry.v1_7_0.LogEntry"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002704 {"@odata.id", "/redfish/v1/Systems/system/"
2705 "LogServices/Crashdump/Entries/" +
2706 logID},
2707 {"Name", "CPU Crashdump"},
2708 {"Id", logID},
2709 {"EntryType", "Oem"},
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002710 {"AdditionalDataURI", std::move(crashdumpURI)},
2711 {"DiagnosticDataType", "OEM"},
2712 {"OEMDiagnosticDataType", "PECICrashdump"},
Johnathan Mantey043a0532020-03-10 17:15:28 -07002713 {"Created", std::move(timestamp)}};
2714 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002715 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002716 std::move(getStoredLogCallback), crashdumpObject,
2717 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002718 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002719}
2720
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002721inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002722{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002723 // Note: Deviated from redfish privilege registry for GET & HEAD
2724 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002725 /**
2726 * Functions triggers appropriate requests on DBus
2727 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002728 BMCWEB_ROUTE(app,
2729 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002730 // This is incorrect, should be.
2731 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002732 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002733 .methods(
2734 boost::beast::http::verb::
2735 get)([](const crow::Request&,
2736 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2737 // Collections don't include the static data added by SubRoute
2738 // because it has a duplicate entry for members
2739 auto getLogEntriesCallback = [asyncResp](
2740 const boost::system::error_code ec,
2741 const std::vector<std::string>&
2742 resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002743 if (ec)
2744 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002745 if (ec.value() !=
2746 boost::system::errc::no_such_file_or_directory)
2747 {
2748 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2749 << ec.message();
2750 messages::internalError(asyncResp->res);
2751 return;
2752 }
Johnathan Mantey043a0532020-03-10 17:15:28 -07002753 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002754 asyncResp->res.jsonValue["@odata.type"] =
2755 "#LogEntryCollection.LogEntryCollection";
2756 asyncResp->res.jsonValue["@odata.id"] =
2757 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2758 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2759 asyncResp->res.jsonValue["Description"] =
2760 "Collection of Crashdump Entries";
2761 nlohmann::json& logEntryArray =
2762 asyncResp->res.jsonValue["Members"];
2763 logEntryArray = nlohmann::json::array();
2764 std::vector<std::string> logIDs;
2765 // Get the list of log entries and build up an empty array big
2766 // enough to hold them
2767 for (const std::string& objpath : resp)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002768 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002769 // Get the log ID
2770 std::size_t lastPos = objpath.rfind('/');
2771 if (lastPos == std::string::npos)
2772 {
2773 continue;
2774 }
2775 logIDs.emplace_back(objpath.substr(lastPos + 1));
Johnathan Mantey043a0532020-03-10 17:15:28 -07002776
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002777 // Add a space for the log entry to the array
2778 logEntryArray.push_back({});
2779 }
2780 // Now go through and set up async calls to fill in the entries
2781 size_t index = 0;
2782 for (const std::string& logID : logIDs)
Johnathan Mantey043a0532020-03-10 17:15:28 -07002783 {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002784 // Add the log entry to the array
2785 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Johnathan Mantey043a0532020-03-10 17:15:28 -07002786 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002787 asyncResp->res.jsonValue["Members@odata.count"] =
2788 logEntryArray.size();
Johnathan Mantey043a0532020-03-10 17:15:28 -07002789 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002790 crow::connections::systemBus->async_method_call(
2791 std::move(getLogEntriesCallback),
2792 "xyz.openbmc_project.ObjectMapper",
2793 "/xyz/openbmc_project/object_mapper",
2794 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2795 std::array<const char*, 1>{crashdumpInterface});
2796 });
2797}
Ed Tanous1da66f72018-07-27 16:13:37 -07002798
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002799inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002800{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002801 // Note: Deviated from redfish privilege registry for GET & HEAD
2802 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002803
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002804 BMCWEB_ROUTE(
2805 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002806 // this is incorrect, should be
2807 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002808 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002809 .methods(boost::beast::http::verb::get)(
2810 [](const crow::Request&,
2811 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2812 const std::string& param) {
2813 const std::string& logID = param;
2814 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2815 });
2816}
Ed Tanous1da66f72018-07-27 16:13:37 -07002817
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002818inline void requestRoutesCrashdumpFile(App& app)
2819{
2820 // Note: Deviated from redfish privilege registry for GET & HEAD
2821 // method for security reasons.
2822 BMCWEB_ROUTE(
2823 app,
2824 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002825 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002826 .methods(boost::beast::http::verb::get)(
2827 [](const crow::Request&,
2828 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2829 const std::string& logID, const std::string& fileName) {
2830 auto getStoredLogCallback =
2831 [asyncResp, logID, fileName](
2832 const boost::system::error_code ec,
Ed Tanous168e20c2021-12-13 14:39:53 -08002833 const std::vector<std::pair<
2834 std::string, dbus::utility::DbusVariantType>>&
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002835 resp) {
2836 if (ec)
2837 {
2838 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2839 << ec.message();
2840 messages::internalError(asyncResp->res);
2841 return;
2842 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002843
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002844 std::string dbusFilename{};
2845 std::string dbusTimestamp{};
2846 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002847
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002848 parseCrashdumpParameters(resp, dbusFilename,
2849 dbusTimestamp, dbusFilepath);
2850
2851 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2852 dbusFilepath.empty())
2853 {
2854 messages::resourceMissingAtURI(asyncResp->res,
2855 fileName);
2856 return;
2857 }
2858
2859 // Verify the file name parameter is correct
2860 if (fileName != dbusFilename)
2861 {
2862 messages::resourceMissingAtURI(asyncResp->res,
2863 fileName);
2864 return;
2865 }
2866
2867 if (!std::filesystem::exists(dbusFilepath))
2868 {
2869 messages::resourceMissingAtURI(asyncResp->res,
2870 fileName);
2871 return;
2872 }
Jason M. Bills2d314912022-01-12 13:59:01 -08002873 std::ifstream ifs(dbusFilepath,
2874 std::ios::in | std::ios::binary);
2875 asyncResp->res.body() = std::string(
2876 std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002877
2878 // Configure this to be a file download when accessed
2879 // from a browser
2880 asyncResp->res.addHeader("Content-Disposition",
2881 "attachment");
2882 };
2883 crow::connections::systemBus->async_method_call(
2884 std::move(getStoredLogCallback), crashdumpObject,
2885 crashdumpPath + std::string("/") + logID,
2886 "org.freedesktop.DBus.Properties", "GetAll",
2887 crashdumpInterface);
2888 });
2889}
2890
2891inline void requestRoutesCrashdumpCollect(App& app)
2892{
2893 // Note: Deviated from redfish privilege registry for GET & HEAD
2894 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08002895 BMCWEB_ROUTE(
2896 app,
2897 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002898 // The below is incorrect; Should be ConfigureManager
2899 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002900 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002901 .methods(
2902 boost::beast::http::verb::
2903 post)([](const crow::Request& req,
2904 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2905 std::string diagnosticDataType;
2906 std::string oemDiagnosticDataType;
2907 if (!redfish::json_util::readJson(
2908 req, asyncResp->res, "DiagnosticDataType",
2909 diagnosticDataType, "OEMDiagnosticDataType",
2910 oemDiagnosticDataType))
James Feist46229572020-02-19 15:11:58 -08002911 {
James Feist46229572020-02-19 15:11:58 -08002912 return;
2913 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002914
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002915 if (diagnosticDataType != "OEM")
2916 {
2917 BMCWEB_LOG_ERROR
2918 << "Only OEM DiagnosticDataType supported for Crashdump";
2919 messages::actionParameterValueFormatError(
2920 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
2921 "CollectDiagnosticData");
2922 return;
2923 }
2924
Ed Tanous98be3e32021-09-16 15:05:36 -07002925 auto collectCrashdumpCallback = [asyncResp,
2926 payload(task::Payload(req))](
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002927 const boost::system::error_code
2928 ec,
Ed Tanous98be3e32021-09-16 15:05:36 -07002929 const std::string&) mutable {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002930 if (ec)
2931 {
2932 if (ec.value() ==
2933 boost::system::errc::operation_not_supported)
2934 {
2935 messages::resourceInStandby(asyncResp->res);
2936 }
2937 else if (ec.value() ==
2938 boost::system::errc::device_or_resource_busy)
2939 {
2940 messages::serviceTemporarilyUnavailable(asyncResp->res,
2941 "60");
2942 }
2943 else
2944 {
2945 messages::internalError(asyncResp->res);
2946 }
2947 return;
2948 }
George Liu0fda0f12021-11-16 10:06:17 +08002949 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2950 [](boost::system::error_code err,
2951 sdbusplus::message::message&,
2952 const std::shared_ptr<task::TaskData>& taskData) {
2953 if (!err)
2954 {
2955 taskData->messages.emplace_back(
2956 messages::taskCompletedOK(
2957 std::to_string(taskData->index)));
2958 taskData->state = "Completed";
2959 }
2960 return task::completed;
2961 },
2962 "type='signal',interface='org.freedesktop.DBus."
2963 "Properties',"
2964 "member='PropertiesChanged',arg0namespace='com.intel.crashdump'");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002965 task->startTimer(std::chrono::minutes(5));
2966 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -07002967 task->payload.emplace(std::move(payload));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002968 };
2969
2970 if (oemDiagnosticDataType == "OnDemand")
2971 {
2972 crow::connections::systemBus->async_method_call(
2973 std::move(collectCrashdumpCallback), crashdumpObject,
2974 crashdumpPath, crashdumpOnDemandInterface,
2975 "GenerateOnDemandLog");
2976 }
2977 else if (oemDiagnosticDataType == "Telemetry")
2978 {
2979 crow::connections::systemBus->async_method_call(
2980 std::move(collectCrashdumpCallback), crashdumpObject,
2981 crashdumpPath, crashdumpTelemetryInterface,
2982 "GenerateTelemetryLog");
2983 }
2984 else
2985 {
2986 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
2987 << oemDiagnosticDataType;
2988 messages::actionParameterValueFormatError(
2989 asyncResp->res, oemDiagnosticDataType,
2990 "OEMDiagnosticDataType", "CollectDiagnosticData");
2991 return;
2992 }
2993 });
2994}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002995
Andrew Geisslercb92c032018-08-17 07:56:14 -07002996/**
2997 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2998 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002999inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003000{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003001 /**
3002 * Function handles POST method request.
3003 * The Clear Log actions does not require any parameter.The action deletes
3004 * all entries found in the Entries collection for this Log Service.
3005 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003006
George Liu0fda0f12021-11-16 10:06:17 +08003007 BMCWEB_ROUTE(
3008 app,
3009 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003010 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003011 .methods(boost::beast::http::verb::post)(
3012 [](const crow::Request&,
3013 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3014 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003015
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003016 // Process response from Logging service.
3017 auto respHandler = [asyncResp](
3018 const boost::system::error_code ec) {
3019 BMCWEB_LOG_DEBUG
3020 << "doClearLog resp_handler callback: Done";
3021 if (ec)
3022 {
3023 // TODO Handle for specific error code
3024 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error "
3025 << ec;
3026 asyncResp->res.result(
3027 boost::beast::http::status::internal_server_error);
3028 return;
3029 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003030
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003031 asyncResp->res.result(
3032 boost::beast::http::status::no_content);
3033 };
3034
3035 // Make call to Logging service to request Clear Log
3036 crow::connections::systemBus->async_method_call(
3037 respHandler, "xyz.openbmc_project.Logging",
3038 "/xyz/openbmc_project/logging",
3039 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3040 });
3041}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003042
3043/****************************************************
3044 * Redfish PostCode interfaces
3045 * using DBUS interface: getPostCodesTS
3046 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003047inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003048{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003049 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003050 .privileges(redfish::privileges::getLogService)
George Liu0fda0f12021-11-16 10:06:17 +08003051 .methods(
3052 boost::beast::http::verb::
3053 get)([](const crow::Request&,
3054 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3055 asyncResp->res.jsonValue = {
3056 {"@odata.id",
3057 "/redfish/v1/Systems/system/LogServices/PostCodes"},
3058 {"@odata.type", "#LogService.v1_1_0.LogService"},
3059 {"Name", "POST Code Log Service"},
3060 {"Description", "POST Code Log Service"},
3061 {"Id", "BIOS POST Code Log"},
3062 {"OverWritePolicy", "WrapsWhenFull"},
3063 {"Entries",
3064 {{"@odata.id",
3065 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
Tejas Patil7c8c4052021-06-04 17:43:14 +05303066
George Liu0fda0f12021-11-16 10:06:17 +08003067 std::pair<std::string, std::string> redfishDateTimeOffset =
3068 crow::utility::getDateTimeOffsetNow();
3069 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3070 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3071 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303072
George Liu0fda0f12021-11-16 10:06:17 +08003073 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3074 {"target",
3075 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
3076 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003077}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003078
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003079inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003080{
George Liu0fda0f12021-11-16 10:06:17 +08003081 BMCWEB_ROUTE(
3082 app,
3083 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003084 // The following privilege is incorrect; It should be ConfigureManager
3085 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003086 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003087 .methods(boost::beast::http::verb::post)(
3088 [](const crow::Request&,
3089 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3090 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003091
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003092 // Make call to post-code service to request clear all
3093 crow::connections::systemBus->async_method_call(
3094 [asyncResp](const boost::system::error_code ec) {
3095 if (ec)
3096 {
3097 // TODO Handle for specific error code
3098 BMCWEB_LOG_ERROR
3099 << "doClearPostCodes resp_handler got error "
3100 << ec;
3101 asyncResp->res.result(boost::beast::http::status::
3102 internal_server_error);
3103 messages::internalError(asyncResp->res);
3104 return;
3105 }
3106 },
3107 "xyz.openbmc_project.State.Boot.PostCode0",
3108 "/xyz/openbmc_project/State/Boot/PostCode0",
3109 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3110 });
3111}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003112
3113static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003114 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303115 const boost::container::flat_map<
3116 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003117 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3118 const uint64_t skip = 0, const uint64_t top = 0)
3119{
3120 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003121 const message_registries::Message* message =
Manojkiran Eda4a0bf532021-04-21 22:46:14 +05303122 message_registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003123
3124 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003125 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003126
3127 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303128 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3129 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003130 {
3131 currentCodeIndex++;
3132 std::string postcodeEntryID =
3133 "B" + std::to_string(bootIndex) + "-" +
3134 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3135
3136 uint64_t usecSinceEpoch = code.first;
3137 uint64_t usTimeOffset = 0;
3138
3139 if (1 == currentCodeIndex)
3140 { // already incremented
3141 firstCodeTimeUs = code.first;
3142 }
3143 else
3144 {
3145 usTimeOffset = code.first - firstCodeTimeUs;
3146 }
3147
3148 // skip if no specific codeIndex is specified and currentCodeIndex does
3149 // not fall between top and skip
3150 if ((codeIndex == 0) &&
3151 (currentCodeIndex <= skip || currentCodeIndex > top))
3152 {
3153 continue;
3154 }
3155
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003156 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003157 // currentIndex
3158 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3159 {
3160 // This is done for simplicity. 1st entry is needed to calculate
3161 // time offset. To improve efficiency, one can get to the entry
3162 // directly (possibly with flatmap's nth method)
3163 continue;
3164 }
3165
3166 // currentCodeIndex is within top and skip or equal to specified code
3167 // index
3168
3169 // Get the Created time from the timestamp
3170 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003171 entryTimeStr =
3172 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003173
3174 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3175 std::ostringstream hexCode;
3176 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303177 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003178 std::ostringstream timeOffsetStr;
3179 // Set Fixed -Point Notation
3180 timeOffsetStr << std::fixed;
3181 // Set precision to 4 digits
3182 timeOffsetStr << std::setprecision(4);
3183 // Add double to stream
3184 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3185 std::vector<std::string> messageArgs = {
3186 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3187
3188 // Get MessageArgs template from message registry
3189 std::string msg;
3190 if (message != nullptr)
3191 {
3192 msg = message->message;
3193
3194 // fill in this post code value
3195 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003196 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003197 {
3198 std::string argStr = "%" + std::to_string(++i);
3199 size_t argPos = msg.find(argStr);
3200 if (argPos != std::string::npos)
3201 {
3202 msg.replace(argPos, argStr.length(), messageArg);
3203 }
3204 }
3205 }
3206
Tim Leed4342a92020-04-27 11:47:58 +08003207 // Get Severity template from message registry
3208 std::string severity;
3209 if (message != nullptr)
3210 {
3211 severity = message->severity;
3212 }
3213
ZhikuiRena3316fc2020-01-29 14:58:08 -08003214 // add to AsyncResp
3215 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003216 nlohmann::json& bmcLogEntry = logEntryArray.back();
George Liu0fda0f12021-11-16 10:06:17 +08003217 bmcLogEntry = {
3218 {"@odata.type", "#LogEntry.v1_8_0.LogEntry"},
3219 {"@odata.id",
3220 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3221 postcodeEntryID},
3222 {"Name", "POST Code Log Entry"},
3223 {"Id", postcodeEntryID},
3224 {"Message", std::move(msg)},
3225 {"MessageId", "OpenBMC.0.2.BIOSPOSTCode"},
3226 {"MessageArgs", std::move(messageArgs)},
3227 {"EntryType", "Event"},
3228 {"Severity", std::move(severity)},
3229 {"Created", entryTimeStr}};
George Liu647b3cd2021-07-05 12:43:56 +08003230 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3231 {
3232 bmcLogEntry["AdditionalDataURI"] =
3233 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3234 postcodeEntryID + "/attachment";
3235 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003236 }
3237}
3238
zhanghch058d1b46d2021-04-01 11:18:24 +08003239static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003240 const uint16_t bootIndex,
3241 const uint64_t codeIndex)
3242{
3243 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303244 [aResp, bootIndex,
3245 codeIndex](const boost::system::error_code ec,
3246 const boost::container::flat_map<
3247 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3248 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003249 if (ec)
3250 {
3251 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3252 messages::internalError(aResp->res);
3253 return;
3254 }
3255
3256 // skip the empty postcode boots
3257 if (postcode.empty())
3258 {
3259 return;
3260 }
3261
3262 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
3263
3264 aResp->res.jsonValue["Members@odata.count"] =
3265 aResp->res.jsonValue["Members"].size();
3266 },
Jonathan Doman15124762021-01-07 17:54:17 -08003267 "xyz.openbmc_project.State.Boot.PostCode0",
3268 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003269 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3270 bootIndex);
3271}
3272
zhanghch058d1b46d2021-04-01 11:18:24 +08003273static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003274 const uint16_t bootIndex,
3275 const uint16_t bootCount,
3276 const uint64_t entryCount, const uint64_t skip,
3277 const uint64_t top)
3278{
3279 crow::connections::systemBus->async_method_call(
3280 [aResp, bootIndex, bootCount, entryCount, skip,
3281 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303282 const boost::container::flat_map<
3283 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3284 postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003285 if (ec)
3286 {
3287 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3288 messages::internalError(aResp->res);
3289 return;
3290 }
3291
3292 uint64_t endCount = entryCount;
3293 if (!postcode.empty())
3294 {
3295 endCount = entryCount + postcode.size();
3296
3297 if ((skip < endCount) && ((top + skip) > entryCount))
3298 {
3299 uint64_t thisBootSkip =
3300 std::max(skip, entryCount) - entryCount;
3301 uint64_t thisBootTop =
3302 std::min(top + skip, endCount) - entryCount;
3303
3304 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
3305 thisBootSkip, thisBootTop);
3306 }
3307 aResp->res.jsonValue["Members@odata.count"] = endCount;
3308 }
3309
3310 // continue to previous bootIndex
3311 if (bootIndex < bootCount)
3312 {
3313 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3314 bootCount, endCount, skip, top);
3315 }
3316 else
3317 {
3318 aResp->res.jsonValue["Members@odata.nextLink"] =
George Liu0fda0f12021-11-16 10:06:17 +08003319 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
ZhikuiRena3316fc2020-01-29 14:58:08 -08003320 std::to_string(skip + top);
3321 }
3322 },
Jonathan Doman15124762021-01-07 17:54:17 -08003323 "xyz.openbmc_project.State.Boot.PostCode0",
3324 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003325 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3326 bootIndex);
3327}
3328
zhanghch058d1b46d2021-04-01 11:18:24 +08003329static void
3330 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3331 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003332{
3333 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003334 sdbusplus::asio::getProperty<uint16_t>(
3335 *crow::connections::systemBus,
3336 "xyz.openbmc_project.State.Boot.PostCode0",
3337 "/xyz/openbmc_project/State/Boot/PostCode0",
3338 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3339 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3340 const uint16_t bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08003341 if (ec)
3342 {
3343 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3344 messages::internalError(aResp->res);
3345 return;
3346 }
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003347 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
3348 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003349}
3350
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003351inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003352{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003353 BMCWEB_ROUTE(app,
3354 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003355 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003356 .methods(boost::beast::http::verb::get)(
3357 [](const crow::Request& req,
3358 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
3359 asyncResp->res.jsonValue["@odata.type"] =
3360 "#LogEntryCollection.LogEntryCollection";
3361 asyncResp->res.jsonValue["@odata.id"] =
3362 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3363 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3364 asyncResp->res.jsonValue["Description"] =
3365 "Collection of POST Code Log Entries";
3366 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3367 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003368
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003369 uint64_t skip = 0;
3370 uint64_t top = maxEntriesPerPage; // Show max entries by default
3371 if (!getSkipParam(asyncResp, req, skip))
3372 {
3373 return;
3374 }
3375 if (!getTopParam(asyncResp, req, top))
3376 {
3377 return;
3378 }
3379 getCurrentBootNumber(asyncResp, skip, top);
3380 });
3381}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003382
George Liu647b3cd2021-07-05 12:43:56 +08003383/**
3384 * @brief Parse post code ID and get the current value and index value
3385 * eg: postCodeID=B1-2, currentValue=1, index=2
3386 *
3387 * @param[in] postCodeID Post Code ID
3388 * @param[out] currentValue Current value
3389 * @param[out] index Index value
3390 *
3391 * @return bool true if the parsing is successful, false the parsing fails
3392 */
3393inline static bool parsePostCode(const std::string& postCodeID,
3394 uint64_t& currentValue, uint16_t& index)
3395{
3396 std::vector<std::string> split;
3397 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3398 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3399 {
3400 return false;
3401 }
3402
Ed Tanousca45aa32022-01-07 09:28:45 -08003403 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003404 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003405 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003406 const char* end = split[0].data() + split[0].size();
3407 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3408
3409 if (ptrIndex != end || ecIndex != std::errc())
3410 {
3411 return false;
3412 }
3413
3414 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003415
3416 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003417 end = split[1].data() + split[1].size();
3418 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
3419 if (ptrValue != end || ecValue != std::errc())
3420 {
3421 return false;
3422 }
3423
3424 return true;
3425}
3426
3427inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3428{
George Liu0fda0f12021-11-16 10:06:17 +08003429 BMCWEB_ROUTE(
3430 app,
3431 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003432 .privileges(redfish::privileges::getLogEntry)
3433 .methods(boost::beast::http::verb::get)(
3434 [](const crow::Request& req,
3435 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3436 const std::string& postCodeID) {
3437 if (!http_helpers::isOctetAccepted(
3438 req.getHeaderValue("Accept")))
3439 {
3440 asyncResp->res.result(
3441 boost::beast::http::status::bad_request);
3442 return;
3443 }
3444
3445 uint64_t currentValue = 0;
3446 uint16_t index = 0;
3447 if (!parsePostCode(postCodeID, currentValue, index))
3448 {
3449 messages::resourceNotFound(asyncResp->res, "LogEntry",
3450 postCodeID);
3451 return;
3452 }
3453
3454 crow::connections::systemBus->async_method_call(
3455 [asyncResp, postCodeID, currentValue](
3456 const boost::system::error_code ec,
3457 const std::vector<std::tuple<
3458 uint64_t, std::vector<uint8_t>>>& postcodes) {
3459 if (ec.value() == EBADR)
3460 {
3461 messages::resourceNotFound(asyncResp->res,
3462 "LogEntry", postCodeID);
3463 return;
3464 }
3465 if (ec)
3466 {
3467 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3468 messages::internalError(asyncResp->res);
3469 return;
3470 }
3471
3472 size_t value = static_cast<size_t>(currentValue) - 1;
3473 if (value == std::string::npos ||
3474 postcodes.size() < currentValue)
3475 {
3476 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3477 messages::resourceNotFound(asyncResp->res,
3478 "LogEntry", postCodeID);
3479 return;
3480 }
3481
Ed Tanous46ff87b2022-01-07 09:25:51 -08003482 auto& [tID, c] = postcodes[value];
3483 if (c.empty())
George Liu647b3cd2021-07-05 12:43:56 +08003484 {
3485 BMCWEB_LOG_INFO << "No found post code data";
3486 messages::resourceNotFound(asyncResp->res,
3487 "LogEntry", postCodeID);
3488 return;
3489 }
Ed Tanous46ff87b2022-01-07 09:25:51 -08003490 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3491 const char* d = reinterpret_cast<const char*>(c.data());
3492 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003493
3494 asyncResp->res.addHeader("Content-Type",
3495 "application/octet-stream");
3496 asyncResp->res.addHeader("Content-Transfer-Encoding",
3497 "Base64");
3498 asyncResp->res.body() =
3499 crow::utility::base64encode(strData);
3500 },
3501 "xyz.openbmc_project.State.Boot.PostCode0",
3502 "/xyz/openbmc_project/State/Boot/PostCode0",
3503 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes",
3504 index);
3505 });
3506}
3507
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003508inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003509{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003510 BMCWEB_ROUTE(
3511 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003512 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003513 .methods(boost::beast::http::verb::get)(
3514 [](const crow::Request&,
3515 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3516 const std::string& targetID) {
George Liu647b3cd2021-07-05 12:43:56 +08003517 uint16_t bootIndex = 0;
3518 uint64_t codeIndex = 0;
3519 if (!parsePostCode(targetID, codeIndex, bootIndex))
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003520 {
3521 // Requested ID was not found
3522 messages::resourceMissingAtURI(asyncResp->res, targetID);
3523 return;
3524 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003525 if (bootIndex == 0 || codeIndex == 0)
3526 {
3527 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3528 << targetID;
3529 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003530
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003531 asyncResp->res.jsonValue["@odata.type"] =
3532 "#LogEntry.v1_4_0.LogEntry";
3533 asyncResp->res.jsonValue["@odata.id"] =
George Liu0fda0f12021-11-16 10:06:17 +08003534 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003535 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3536 asyncResp->res.jsonValue["Description"] =
3537 "Collection of POST Code Log Entries";
3538 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3539 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003540
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003541 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3542 });
3543}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003544
Ed Tanous1da66f72018-07-27 16:13:37 -07003545} // namespace redfish