blob: c118deb4b1f35eed1dfc98d0b2e9ebba2af5c089 [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 Tanous45ca1b82022-03-25 13:07:27 -070037#include <query.hpp>
Ed Tanoused398212021-06-09 17:05:54 -070038#include <registries/privilege_registry.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039
George Liu647b3cd2021-07-05 12:43:56 +080040#include <charconv>
James Feist4418c7f2019-04-15 11:09:15 -070041#include <filesystem>
Xiaochao Ma75710de2021-01-21 17:56:02 +080042#include <optional>
Ed Tanous26702d02021-11-03 15:02:33 -070043#include <span>
Jason M. Billscd225da2019-05-08 15:31:57 -070044#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080045#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070046
47namespace redfish
48{
49
Gunnar Mills1214b7e2020-06-04 10:11:30 -050050constexpr char const* crashdumpObject = "com.intel.crashdump";
51constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050052constexpr char const* crashdumpInterface = "com.intel.crashdump";
53constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070054 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050055constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070056 "com.intel.crashdump.OnDemand";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070057constexpr char const* crashdumpTelemetryInterface =
58 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070059
Ed Tanousfffb8c12022-02-07 23:53:03 -080060namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -070061{
Ed Tanous26702d02021-11-03 15:02:33 -070062static const Message*
63 getMessageFromRegistry(const std::string& messageKey,
64 const std::span<const MessageEntry> registry)
Jason M. Bills4851d452019-03-28 11:27:48 -070065{
Ed Tanous002d39b2022-05-31 08:59:27 -070066 std::span<const MessageEntry>::iterator messageIt =
67 std::find_if(registry.begin(), registry.end(),
68 [&messageKey](const MessageEntry& messageEntry) {
69 return std::strcmp(messageEntry.first, messageKey.c_str()) == 0;
Ed Tanous26702d02021-11-03 15:02:33 -070070 });
71 if (messageIt != registry.end())
Jason M. Bills4851d452019-03-28 11:27:48 -070072 {
73 return &messageIt->second;
74 }
75
76 return nullptr;
77}
78
Gunnar Mills1214b7e2020-06-04 10:11:30 -050079static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070080{
81 // Redfish MessageIds are in the form
82 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
83 // the right Message
84 std::vector<std::string> fields;
85 fields.reserve(4);
86 boost::split(fields, messageID, boost::is_any_of("."));
Ed Tanous02cad962022-06-30 16:50:15 -070087 const std::string& registryName = fields[0];
88 const std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070089
90 // Find the right registry and check it for the MessageKey
91 if (std::string(base::header.registryPrefix) == registryName)
92 {
93 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070094 messageKey, std::span<const MessageEntry>(base::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -070095 }
96 if (std::string(openbmc::header.registryPrefix) == registryName)
97 {
98 return getMessageFromRegistry(
Ed Tanous26702d02021-11-03 15:02:33 -070099 messageKey, std::span<const MessageEntry>(openbmc::registry));
Jason M. Bills4851d452019-03-28 11:27:48 -0700100 }
101 return nullptr;
102}
Ed Tanousfffb8c12022-02-07 23:53:03 -0800103} // namespace registries
Jason M. Bills4851d452019-03-28 11:27:48 -0700104
James Feistf6150402019-01-08 10:36:20 -0800105namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700106
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500107inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700108{
Ed Tanousd4d25792020-09-29 15:15:03 -0700109 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
110 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
111 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
112 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700113 {
114 return "Critical";
115 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700116 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
117 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
118 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
Andrew Geisslercb92c032018-08-17 07:56:14 -0700119 {
120 return "OK";
121 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700122 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
Andrew Geisslercb92c032018-08-17 07:56:14 -0700123 {
124 return "Warning";
125 }
126 return "";
127}
128
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700129inline static int getJournalMetadata(sd_journal* journal,
130 const std::string_view& field,
131 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700132{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500133 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700134 size_t length = 0;
135 int ret = 0;
136 // Get the metadata from the requested field of the journal entry
Ed Tanous46ff87b2022-01-07 09:25:51 -0800137 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
138 const void** dataVoid = reinterpret_cast<const void**>(&data);
139
140 ret = sd_journal_get_data(journal, field.data(), dataVoid, &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700141 if (ret < 0)
142 {
143 return ret;
144 }
Ed Tanous39e77502019-03-04 17:35:53 -0800145 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700146 // Only use the content after the "=" character.
Ed Tanous81ce6092020-12-17 16:54:55 +0000147 contents.remove_prefix(std::min(contents.find('=') + 1, contents.size()));
Jason M. Bills16428a12018-11-02 12:42:29 -0700148 return ret;
149}
150
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700151inline static int getJournalMetadata(sd_journal* journal,
152 const std::string_view& field,
153 const int& base, long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700154{
155 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800156 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700157 // Get the metadata from the requested field of the journal entry
158 ret = getJournalMetadata(journal, field, metadata);
159 if (ret < 0)
160 {
161 return ret;
162 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000163 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700164 return ret;
165}
166
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700167inline static bool getEntryTimestamp(sd_journal* journal,
168 std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800169{
170 int ret = 0;
171 uint64_t timestamp = 0;
172 ret = sd_journal_get_realtime_usec(journal, &timestamp);
173 if (ret < 0)
174 {
175 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
176 << strerror(-ret);
177 return false;
178 }
Nan Zhou1d8782e2021-11-29 22:23:18 -0800179 entryTimestamp = crow::utility::getDateTimeUint(timestamp / 1000 / 1000);
Asmitha Karunanithi9c620e22020-08-02 11:55:21 -0500180 return true;
ZhikuiRena3316fc2020-01-29 14:58:08 -0800181}
Ed Tanous50b8a432022-02-03 16:29:50 -0800182
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700183inline static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
184 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700185{
186 int ret = 0;
187 static uint64_t prevTs = 0;
188 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700189 if (firstEntry)
190 {
191 prevTs = 0;
192 }
193
Jason M. Bills16428a12018-11-02 12:42:29 -0700194 // Get the entry timestamp
195 uint64_t curTs = 0;
196 ret = sd_journal_get_realtime_usec(journal, &curTs);
197 if (ret < 0)
198 {
199 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
200 << strerror(-ret);
201 return false;
202 }
203 // If the timestamp isn't unique, increment the index
204 if (curTs == prevTs)
205 {
206 index++;
207 }
208 else
209 {
210 // Otherwise, reset it
211 index = 0;
212 }
213 // Save the timestamp
214 prevTs = curTs;
215
216 entryID = std::to_string(curTs);
217 if (index > 0)
218 {
219 entryID += "_" + std::to_string(index);
220 }
221 return true;
222}
223
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500224static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700225 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700226{
Ed Tanous271584a2019-07-09 16:24:22 -0700227 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700228 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700229 if (firstEntry)
230 {
231 prevTs = 0;
232 }
233
Jason M. Bills95820182019-04-22 16:25:34 -0700234 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700235 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700236 std::tm timeStruct = {};
237 std::istringstream entryStream(logEntry);
238 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
239 {
240 curTs = std::mktime(&timeStruct);
241 }
242 // If the timestamp isn't unique, increment the index
243 if (curTs == prevTs)
244 {
245 index++;
246 }
247 else
248 {
249 // Otherwise, reset it
250 index = 0;
251 }
252 // Save the timestamp
253 prevTs = curTs;
254
255 entryID = std::to_string(curTs);
256 if (index > 0)
257 {
258 entryID += "_" + std::to_string(index);
259 }
260 return true;
261}
262
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700263inline static bool
zhanghch058d1b46d2021-04-01 11:18:24 +0800264 getTimestampFromID(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
265 const std::string& entryID, uint64_t& timestamp,
266 uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700267{
268 if (entryID.empty())
269 {
270 return false;
271 }
272 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800273 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700274
Ed Tanous81ce6092020-12-17 16:54:55 +0000275 auto underscorePos = tsStr.find('_');
Ed Tanous71d5d8d2022-01-25 11:04:33 -0800276 if (underscorePos != std::string_view::npos)
Jason M. Bills16428a12018-11-02 12:42:29 -0700277 {
278 // Timestamp has an index
279 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800280 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700281 indexStr.remove_prefix(underscorePos + 1);
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700282 auto [ptr, ec] = std::from_chars(
283 indexStr.data(), indexStr.data() + indexStr.size(), index);
284 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700285 {
Ed Tanousace85d62021-10-26 12:45:59 -0700286 messages::resourceMissingAtURI(
287 asyncResp->res, crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700288 return false;
289 }
290 }
291 // Timestamp has no index
Ed Tanousc0bd5e42021-09-13 17:00:19 -0700292 auto [ptr, ec] =
293 std::from_chars(tsStr.data(), tsStr.data() + tsStr.size(), timestamp);
294 if (ec != std::errc())
Jason M. Bills16428a12018-11-02 12:42:29 -0700295 {
Ed Tanousace85d62021-10-26 12:45:59 -0700296 messages::resourceMissingAtURI(asyncResp->res,
297 crow::utility::urlFromPieces(entryID));
Jason M. Bills16428a12018-11-02 12:42:29 -0700298 return false;
299 }
300 return true;
301}
302
Jason M. Bills95820182019-04-22 16:25:34 -0700303static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500304 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700305{
306 static const std::filesystem::path redfishLogDir = "/var/log";
307 static const std::string redfishLogFilename = "redfish";
308
309 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500310 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700311 std::filesystem::directory_iterator(redfishLogDir))
312 {
313 // If we find a redfish log file, save the path
314 std::string filename = dirEnt.path().filename();
315 if (boost::starts_with(filename, redfishLogFilename))
316 {
317 redfishLogFiles.emplace_back(redfishLogDir / filename);
318 }
319 }
320 // As the log files rotate, they are appended with a ".#" that is higher for
321 // the older logs. Since we don't expect more than 10 log files, we
322 // can just sort the list to get them in order from newest to oldest
323 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
324
325 return !redfishLogFiles.empty();
326}
327
Nan Zhou21ab4042022-06-26 23:07:40 +0000328static std::string getDumpEntriesPath(const std::string& dumpType)
Claire Weinanfdd26902022-03-01 14:18:25 -0800329{
330 std::string entriesPath;
331
332 if (dumpType == "BMC")
333 {
334 entriesPath = "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/";
335 }
336 else if (dumpType == "FaultLog")
337 {
338 entriesPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/";
339 }
340 else if (dumpType == "System")
341 {
342 entriesPath = "/redfish/v1/Systems/system/LogServices/Dump/Entries/";
343 }
344 else
345 {
346 BMCWEB_LOG_ERROR << "getDumpEntriesPath() invalid dump type: "
347 << dumpType;
348 }
349
350 // Returns empty string on error
351 return entriesPath;
352}
353
zhanghch058d1b46d2021-04-01 11:18:24 +0800354inline void
355 getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
356 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500357{
Claire Weinanfdd26902022-03-01 14:18:25 -0800358 std::string entriesPath = getDumpEntriesPath(dumpType);
359 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500360 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500361 messages::internalError(asyncResp->res);
362 return;
363 }
364
365 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800366 [asyncResp, entriesPath,
Ed Tanous711ac7a2021-12-20 09:34:41 -0800367 dumpType](const boost::system::error_code ec,
368 dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700369 if (ec)
370 {
371 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
372 messages::internalError(asyncResp->res);
373 return;
374 }
375
Claire Weinanfdd26902022-03-01 14:18:25 -0800376 // Remove ending slash
377 std::string odataIdStr = entriesPath;
378 if (!odataIdStr.empty())
379 {
380 odataIdStr.pop_back();
381 }
382
383 asyncResp->res.jsonValue["@odata.type"] =
384 "#LogEntryCollection.LogEntryCollection";
385 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
386 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
387 asyncResp->res.jsonValue["Description"] =
388 "Collection of " + dumpType + " Dump Entries";
389
Ed Tanous002d39b2022-05-31 08:59:27 -0700390 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
391 entriesArray = nlohmann::json::array();
392 std::string dumpEntryPath =
393 "/xyz/openbmc_project/dump/" +
394 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
395
396 std::sort(resp.begin(), resp.end(), [](const auto& l, const auto& r) {
397 return AlphanumLess<std::string>()(l.first.filename(),
398 r.first.filename());
399 });
400
401 for (auto& object : resp)
402 {
403 if (object.first.str.find(dumpEntryPath) == std::string::npos)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500404 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700405 continue;
406 }
407 uint64_t timestamp = 0;
408 uint64_t size = 0;
409 std::string dumpStatus;
410 nlohmann::json thisEntry;
411
412 std::string entryID = object.first.filename();
413 if (entryID.empty())
414 {
415 continue;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500416 }
417
Ed Tanous002d39b2022-05-31 08:59:27 -0700418 for (auto& interfaceMap : object.second)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500419 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700420 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500421 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700422 for (const auto& propertyMap : interfaceMap.second)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500423 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700424 if (propertyMap.first == "Status")
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500425 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700426 const auto* status =
427 std::get_if<std::string>(&propertyMap.second);
428 if (status == nullptr)
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500429 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700430 messages::internalError(asyncResp->res);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500431 break;
432 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700433 dumpStatus = *status;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500434 }
435 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700436 }
437 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
438 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500439
Ed Tanous002d39b2022-05-31 08:59:27 -0700440 for (auto& propertyMap : interfaceMap.second)
441 {
442 if (propertyMap.first == "Size")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500443 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700444 const auto* sizePtr =
445 std::get_if<uint64_t>(&propertyMap.second);
446 if (sizePtr == nullptr)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500447 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700448 messages::internalError(asyncResp->res);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500449 break;
450 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700451 size = *sizePtr;
452 break;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500453 }
454 }
455 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700456 else if (interfaceMap.first ==
457 "xyz.openbmc_project.Time.EpochTime")
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500458 {
Asmitha Karunanithi35440d12021-09-07 11:17:57 -0500459
Ed Tanous002d39b2022-05-31 08:59:27 -0700460 for (const auto& propertyMap : interfaceMap.second)
461 {
462 if (propertyMap.first == "Elapsed")
463 {
464 const uint64_t* usecsTimeStamp =
465 std::get_if<uint64_t>(&propertyMap.second);
466 if (usecsTimeStamp == nullptr)
467 {
468 messages::internalError(asyncResp->res);
469 break;
470 }
471 timestamp = (*usecsTimeStamp / 1000 / 1000);
472 break;
473 }
474 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500475 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500476 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700477
478 if (dumpStatus !=
479 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
480 !dumpStatus.empty())
481 {
482 // Dump status is not Complete, no need to enumerate
483 continue;
484 }
485
486 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800487 thisEntry["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700488 thisEntry["Id"] = entryID;
489 thisEntry["EntryType"] = "Event";
490 thisEntry["Created"] = crow::utility::getDateTimeUint(timestamp);
491 thisEntry["Name"] = dumpType + " Dump Entry";
492
Ed Tanous002d39b2022-05-31 08:59:27 -0700493 if (dumpType == "BMC")
494 {
495 thisEntry["DiagnosticDataType"] = "Manager";
496 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800497 entriesPath + entryID + "/attachment";
498 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700499 }
500 else if (dumpType == "System")
501 {
502 thisEntry["DiagnosticDataType"] = "OEM";
503 thisEntry["OEMDiagnosticDataType"] = "System";
504 thisEntry["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800505 entriesPath + entryID + "/attachment";
506 thisEntry["AdditionalDataSizeBytes"] = size;
Ed Tanous002d39b2022-05-31 08:59:27 -0700507 }
508 entriesArray.push_back(std::move(thisEntry));
509 }
510 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500511 },
512 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
513 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
514}
515
zhanghch058d1b46d2021-04-01 11:18:24 +0800516inline void
Claire Weinanc7a6d662022-06-13 16:36:39 -0700517 getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
zhanghch058d1b46d2021-04-01 11:18:24 +0800518 const std::string& entryID, const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500519{
Claire Weinanfdd26902022-03-01 14:18:25 -0800520 std::string entriesPath = getDumpEntriesPath(dumpType);
521 if (entriesPath.empty())
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500522 {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500523 messages::internalError(asyncResp->res);
524 return;
525 }
526
527 crow::connections::systemBus->async_method_call(
Claire Weinanfdd26902022-03-01 14:18:25 -0800528 [asyncResp, entryID, dumpType,
529 entriesPath](const boost::system::error_code ec,
Ed Tanous02cad962022-06-30 16:50:15 -0700530 const dbus::utility::ManagedObjectType& resp) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700531 if (ec)
532 {
533 BMCWEB_LOG_ERROR << "DumpEntry resp_handler got error " << ec;
534 messages::internalError(asyncResp->res);
535 return;
536 }
537
538 bool foundDumpEntry = false;
539 std::string dumpEntryPath =
540 "/xyz/openbmc_project/dump/" +
541 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/";
542
543 for (const auto& objectPath : resp)
544 {
545 if (objectPath.first.str != dumpEntryPath + entryID)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500546 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700547 continue;
548 }
549
550 foundDumpEntry = true;
551 uint64_t timestamp = 0;
552 uint64_t size = 0;
553 std::string dumpStatus;
554
555 for (const auto& interfaceMap : objectPath.second)
556 {
557 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
558 {
559 for (const auto& propertyMap : interfaceMap.second)
560 {
561 if (propertyMap.first == "Status")
562 {
563 const std::string* status =
564 std::get_if<std::string>(&propertyMap.second);
565 if (status == nullptr)
566 {
567 messages::internalError(asyncResp->res);
568 break;
569 }
570 dumpStatus = *status;
571 }
572 }
573 }
574 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
575 {
576 for (const auto& propertyMap : interfaceMap.second)
577 {
578 if (propertyMap.first == "Size")
579 {
580 const uint64_t* sizePtr =
581 std::get_if<uint64_t>(&propertyMap.second);
582 if (sizePtr == nullptr)
583 {
584 messages::internalError(asyncResp->res);
585 break;
586 }
587 size = *sizePtr;
588 break;
589 }
590 }
591 }
592 else if (interfaceMap.first ==
593 "xyz.openbmc_project.Time.EpochTime")
594 {
595 for (const auto& propertyMap : interfaceMap.second)
596 {
597 if (propertyMap.first == "Elapsed")
598 {
599 const uint64_t* usecsTimeStamp =
600 std::get_if<uint64_t>(&propertyMap.second);
601 if (usecsTimeStamp == nullptr)
602 {
603 messages::internalError(asyncResp->res);
604 break;
605 }
606 timestamp = *usecsTimeStamp / 1000 / 1000;
607 break;
608 }
609 }
610 }
611 }
612
613 if (dumpStatus !=
614 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
615 !dumpStatus.empty())
616 {
617 // Dump status is not Complete
618 // return not found until status is changed to Completed
619 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
620 entryID);
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500621 return;
622 }
623
Ed Tanous002d39b2022-05-31 08:59:27 -0700624 asyncResp->res.jsonValue["@odata.type"] =
625 "#LogEntry.v1_8_0.LogEntry";
Claire Weinanfdd26902022-03-01 14:18:25 -0800626 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
Ed Tanous002d39b2022-05-31 08:59:27 -0700627 asyncResp->res.jsonValue["Id"] = entryID;
628 asyncResp->res.jsonValue["EntryType"] = "Event";
629 asyncResp->res.jsonValue["Created"] =
630 crow::utility::getDateTimeUint(timestamp);
631 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500632
Ed Tanous002d39b2022-05-31 08:59:27 -0700633 if (dumpType == "BMC")
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500634 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700635 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
636 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800637 entriesPath + entryID + "/attachment";
638 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500639 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700640 else if (dumpType == "System")
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500641 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700642 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
643 asyncResp->res.jsonValue["OEMDiagnosticDataType"] = "System";
644 asyncResp->res.jsonValue["AdditionalDataURI"] =
Claire Weinanfdd26902022-03-01 14:18:25 -0800645 entriesPath + entryID + "/attachment";
646 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500647 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700648 }
649 if (!foundDumpEntry)
650 {
651 BMCWEB_LOG_ERROR << "Can't find Dump Entry";
652 messages::internalError(asyncResp->res);
653 return;
654 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500655 },
656 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
657 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
658}
659
zhanghch058d1b46d2021-04-01 11:18:24 +0800660inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
Stanley Chu98782562020-11-04 16:10:24 +0800661 const std::string& entryID,
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500662 const std::string& dumpType)
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500663{
Ed Tanous002d39b2022-05-31 08:59:27 -0700664 auto respHandler =
665 [asyncResp, entryID](const boost::system::error_code ec) {
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500666 BMCWEB_LOG_DEBUG << "Dump Entry doDelete callback: Done";
667 if (ec)
668 {
George Liu3de8d8b2021-03-22 17:49:39 +0800669 if (ec.value() == EBADR)
670 {
671 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
672 return;
673 }
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500674 BMCWEB_LOG_ERROR << "Dump (DBus) doDelete respHandler got error "
Claire Weinanfdd26902022-03-01 14:18:25 -0800675 << ec << " entryID=" << entryID;
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500676 messages::internalError(asyncResp->res);
677 return;
678 }
679 };
680 crow::connections::systemBus->async_method_call(
681 respHandler, "xyz.openbmc_project.Dump.Manager",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500682 "/xyz/openbmc_project/dump/" +
683 std::string(boost::algorithm::to_lower_copy(dumpType)) + "/entry/" +
684 entryID,
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500685 "xyz.openbmc_project.Object.Delete", "Delete");
686}
687
zhanghch058d1b46d2021-04-01 11:18:24 +0800688inline void
Ed Tanous98be3e32021-09-16 15:05:36 -0700689 createDumpTaskCallback(task::Payload&& payload,
zhanghch058d1b46d2021-04-01 11:18:24 +0800690 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
691 const uint32_t& dumpId, const std::string& dumpPath,
692 const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500693{
694 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500695 [dumpId, dumpPath, dumpType](
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500696 boost::system::error_code err, sdbusplus::message::message& m,
697 const std::shared_ptr<task::TaskData>& taskData) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700698 if (err)
699 {
700 BMCWEB_LOG_ERROR << "Error in creating a dump";
701 taskData->state = "Cancelled";
Asmitha Karunanithi6145ed62020-09-17 23:40:03 -0500702 return task::completed;
Ed Tanous002d39b2022-05-31 08:59:27 -0700703 }
704
705 dbus::utility::DBusInteracesMap interfacesList;
706
707 sdbusplus::message::object_path objPath;
708
709 m.read(objPath, interfacesList);
710
711 if (objPath.str ==
712 "/xyz/openbmc_project/dump/" +
713 std::string(boost::algorithm::to_lower_copy(dumpType)) +
714 "/entry/" + std::to_string(dumpId))
715 {
716 nlohmann::json retMessage = messages::success();
717 taskData->messages.emplace_back(retMessage);
718
719 std::string headerLoc =
720 "Location: " + dumpPath + std::to_string(dumpId);
721 taskData->payload->httpHeaders.emplace_back(std::move(headerLoc));
722
723 taskData->state = "Completed";
724 return task::completed;
725 }
726 return task::completed;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500727 },
Jason M. Bills4978b632022-02-22 14:17:43 -0800728 "type='signal',interface='org.freedesktop.DBus.ObjectManager',"
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500729 "member='InterfacesAdded', "
730 "path='/xyz/openbmc_project/dump'");
731
732 task->startTimer(std::chrono::minutes(3));
733 task->populateResp(asyncResp->res);
Ed Tanous98be3e32021-09-16 15:05:36 -0700734 task->payload.emplace(std::move(payload));
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500735}
736
zhanghch058d1b46d2021-04-01 11:18:24 +0800737inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
738 const crow::Request& req, const std::string& dumpType)
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500739{
Claire Weinanfdd26902022-03-01 14:18:25 -0800740 std::string dumpPath = getDumpEntriesPath(dumpType);
741 if (dumpPath.empty())
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500742 {
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500743 messages::internalError(asyncResp->res);
744 return;
745 }
746
747 std::optional<std::string> diagnosticDataType;
748 std::optional<std::string> oemDiagnosticDataType;
749
Willy Tu15ed6782021-12-14 11:03:16 -0800750 if (!redfish::json_util::readJsonAction(
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500751 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
752 "OEMDiagnosticDataType", oemDiagnosticDataType))
753 {
754 return;
755 }
756
757 if (dumpType == "System")
758 {
759 if (!oemDiagnosticDataType || !diagnosticDataType)
760 {
Jason M. Bills4978b632022-02-22 14:17:43 -0800761 BMCWEB_LOG_ERROR
762 << "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500763 messages::actionParameterMissing(
764 asyncResp->res, "CollectDiagnosticData",
765 "DiagnosticDataType & OEMDiagnosticDataType");
766 return;
767 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700768 if ((*oemDiagnosticDataType != "System") ||
769 (*diagnosticDataType != "OEM"))
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500770 {
771 BMCWEB_LOG_ERROR << "Wrong parameter values passed";
Ed Tanousace85d62021-10-26 12:45:59 -0700772 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500773 return;
774 }
775 }
776 else if (dumpType == "BMC")
777 {
778 if (!diagnosticDataType)
779 {
George Liu0fda0f12021-11-16 10:06:17 +0800780 BMCWEB_LOG_ERROR
781 << "CreateDump action parameter 'DiagnosticDataType' not found!";
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500782 messages::actionParameterMissing(
783 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
784 return;
785 }
Ed Tanous3174e4d2020-10-07 11:41:22 -0700786 if (*diagnosticDataType != "Manager")
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500787 {
788 BMCWEB_LOG_ERROR
789 << "Wrong parameter value passed for 'DiagnosticDataType'";
Ed Tanousace85d62021-10-26 12:45:59 -0700790 messages::internalError(asyncResp->res);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500791 return;
792 }
793 }
794
795 crow::connections::systemBus->async_method_call(
Ed Tanous98be3e32021-09-16 15:05:36 -0700796 [asyncResp, payload(task::Payload(req)), dumpPath,
797 dumpType](const boost::system::error_code ec,
798 const uint32_t& dumpId) mutable {
Ed Tanous002d39b2022-05-31 08:59:27 -0700799 if (ec)
800 {
801 BMCWEB_LOG_ERROR << "CreateDump resp_handler got error " << ec;
802 messages::internalError(asyncResp->res);
803 return;
804 }
805 BMCWEB_LOG_DEBUG << "Dump Created. Id: " << dumpId;
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500806
Ed Tanous002d39b2022-05-31 08:59:27 -0700807 createDumpTaskCallback(std::move(payload), asyncResp, dumpId, dumpPath,
808 dumpType);
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500809 },
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500810 "xyz.openbmc_project.Dump.Manager",
811 "/xyz/openbmc_project/dump/" +
812 std::string(boost::algorithm::to_lower_copy(dumpType)),
Asmitha Karunanithia43be802020-05-07 05:05:36 -0500813 "xyz.openbmc_project.Dump.Create", "CreateDump");
814}
815
zhanghch058d1b46d2021-04-01 11:18:24 +0800816inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
817 const std::string& dumpType)
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500818{
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500819 std::string dumpTypeLowerCopy =
820 std::string(boost::algorithm::to_lower_copy(dumpType));
zhanghch058d1b46d2021-04-01 11:18:24 +0800821
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500822 crow::connections::systemBus->async_method_call(
Ed Tanousb9d36b42022-02-26 21:42:46 -0800823 [asyncResp, dumpType](
824 const boost::system::error_code ec,
825 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
Ed Tanous002d39b2022-05-31 08:59:27 -0700826 if (ec)
827 {
828 BMCWEB_LOG_ERROR << "resp_handler got error " << ec;
829 messages::internalError(asyncResp->res);
830 return;
831 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500832
Ed Tanous002d39b2022-05-31 08:59:27 -0700833 for (const std::string& path : subTreePaths)
834 {
835 sdbusplus::message::object_path objPath(path);
836 std::string logID = objPath.filename();
837 if (logID.empty())
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500838 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700839 continue;
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500840 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700841 deleteDumpEntry(asyncResp, logID, dumpType);
842 }
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500843 },
844 "xyz.openbmc_project.ObjectMapper",
845 "/xyz/openbmc_project/object_mapper",
846 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
Asmitha Karunanithib47452b2020-09-25 02:02:19 -0500847 "/xyz/openbmc_project/dump/" + dumpTypeLowerCopy, 0,
848 std::array<std::string, 1>{"xyz.openbmc_project.Dump.Entry." +
849 dumpType});
Asmitha Karunanithi80319af2020-05-07 05:30:21 -0500850}
851
Ed Tanousb9d36b42022-02-26 21:42:46 -0800852inline static void
853 parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap& params,
854 std::string& filename, std::string& timestamp,
855 std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700856{
857 for (auto property : params)
858 {
859 if (property.first == "Timestamp")
860 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500861 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500862 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700863 if (value != nullptr)
864 {
865 timestamp = *value;
866 }
867 }
868 else if (property.first == "Filename")
869 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500870 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500871 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700872 if (value != nullptr)
873 {
874 filename = *value;
875 }
876 }
877 else if (property.first == "Log")
878 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500879 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500880 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700881 if (value != nullptr)
882 {
883 logfile = *value;
884 }
885 }
886 }
887}
888
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500889constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700890inline void requestRoutesSystemLogServiceCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -0700891{
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800892 /**
893 * Functions triggers appropriate requests on DBus
894 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700895 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/")
Ed Tanoused398212021-06-09 17:05:54 -0700896 .privileges(redfish::privileges::getLogServiceCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -0700897 .methods(boost::beast::http::verb::get)(
898 [&app](const crow::Request& req,
899 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000900 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700901 {
902 return;
903 }
904 // Collections don't include the static data added by SubRoute
905 // because it has a duplicate entry for members
906 asyncResp->res.jsonValue["@odata.type"] =
907 "#LogServiceCollection.LogServiceCollection";
908 asyncResp->res.jsonValue["@odata.id"] =
909 "/redfish/v1/Systems/system/LogServices";
910 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
911 asyncResp->res.jsonValue["Description"] =
912 "Collection of LogServices for this Computer System";
913 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
914 logServiceArray = nlohmann::json::array();
915 nlohmann::json::object_t eventLog;
916 eventLog["@odata.id"] =
917 "/redfish/v1/Systems/system/LogServices/EventLog";
918 logServiceArray.push_back(std::move(eventLog));
Asmitha Karunanithi5cb1dd22020-05-07 04:35:02 -0500919#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700920 nlohmann::json::object_t dumpLog;
921 dumpLog["@odata.id"] = "/redfish/v1/Systems/system/LogServices/Dump";
922 logServiceArray.push_back(std::move(dumpLog));
raviteja-bc9bb6862020-02-03 11:53:32 -0600923#endif
924
Jason M. Billsd53dd412019-02-12 17:16:22 -0800925#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
Ed Tanous002d39b2022-05-31 08:59:27 -0700926 nlohmann::json::object_t crashdump;
927 crashdump["@odata.id"] =
928 "/redfish/v1/Systems/system/LogServices/Crashdump";
929 logServiceArray.push_back(std::move(crashdump));
Jason M. Billsd53dd412019-02-12 17:16:22 -0800930#endif
Spencer Kub7028eb2021-10-26 15:27:35 +0800931
932#ifdef BMCWEB_ENABLE_REDFISH_HOST_LOGGER
Ed Tanous002d39b2022-05-31 08:59:27 -0700933 nlohmann::json::object_t hostlogger;
934 hostlogger["@odata.id"] =
935 "/redfish/v1/Systems/system/LogServices/HostLogger";
936 logServiceArray.push_back(std::move(hostlogger));
Spencer Kub7028eb2021-10-26 15:27:35 +0800937#endif
Ed Tanous002d39b2022-05-31 08:59:27 -0700938 asyncResp->res.jsonValue["Members@odata.count"] =
939 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800940
Ed Tanous002d39b2022-05-31 08:59:27 -0700941 crow::connections::systemBus->async_method_call(
942 [asyncResp](const boost::system::error_code ec,
943 const dbus::utility::MapperGetSubTreePathsResponse&
944 subtreePath) {
945 if (ec)
946 {
947 BMCWEB_LOG_ERROR << ec;
948 return;
949 }
Ed Tanous45ca1b82022-03-25 13:07:27 -0700950
Ed Tanous002d39b2022-05-31 08:59:27 -0700951 for (const auto& pathStr : subtreePath)
952 {
953 if (pathStr.find("PostCode") != std::string::npos)
954 {
955 nlohmann::json& logServiceArrayLocal =
956 asyncResp->res.jsonValue["Members"];
957 logServiceArrayLocal.push_back(
958 {{"@odata.id",
959 "/redfish/v1/Systems/system/LogServices/PostCodes"}});
960 asyncResp->res.jsonValue["Members@odata.count"] =
961 logServiceArrayLocal.size();
962 return;
963 }
964 }
965 },
966 "xyz.openbmc_project.ObjectMapper",
967 "/xyz/openbmc_project/object_mapper",
968 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
969 std::array<const char*, 1>{postCodeIface});
Ed Tanous45ca1b82022-03-25 13:07:27 -0700970 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -0700971}
972
973inline void requestRoutesEventLogService(App& app)
974{
975 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Ed Tanoused398212021-06-09 17:05:54 -0700976 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -0700977 .methods(boost::beast::http::verb::get)(
978 [&app](const crow::Request& req,
979 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +0000980 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -0700981 {
982 return;
983 }
984 asyncResp->res.jsonValue["@odata.id"] =
985 "/redfish/v1/Systems/system/LogServices/EventLog";
986 asyncResp->res.jsonValue["@odata.type"] =
987 "#LogService.v1_1_0.LogService";
988 asyncResp->res.jsonValue["Name"] = "Event Log Service";
989 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
990 asyncResp->res.jsonValue["Id"] = "EventLog";
991 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +0530992
Ed Tanous002d39b2022-05-31 08:59:27 -0700993 std::pair<std::string, std::string> redfishDateTimeOffset =
994 crow::utility::getDateTimeOffsetNow();
Tejas Patil7c8c4052021-06-04 17:43:14 +0530995
Ed Tanous002d39b2022-05-31 08:59:27 -0700996 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
997 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
998 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +0530999
Ed Tanous002d39b2022-05-31 08:59:27 -07001000 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1001 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1002 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001003
Ed Tanous002d39b2022-05-31 08:59:27 -07001004 {"target",
1005 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog"}};
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001006 });
1007}
1008
1009inline void requestRoutesJournalEventLogClear(App& app)
1010{
Jason M. Bills4978b632022-02-22 14:17:43 -08001011 BMCWEB_ROUTE(
1012 app,
1013 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanous432a8902021-06-14 15:28:56 -07001014 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001015 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001016 [&app](const crow::Request& req,
1017 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001018 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001019 {
1020 return;
1021 }
1022 // Clear the EventLog by deleting the log files
1023 std::vector<std::filesystem::path> redfishLogFiles;
1024 if (getRedfishLogFiles(redfishLogFiles))
1025 {
1026 for (const std::filesystem::path& file : redfishLogFiles)
1027 {
1028 std::error_code ec;
1029 std::filesystem::remove(file, ec);
1030 }
1031 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001032
Ed Tanous002d39b2022-05-31 08:59:27 -07001033 // Reload rsyslog so it knows to start new log files
1034 crow::connections::systemBus->async_method_call(
1035 [asyncResp](const boost::system::error_code ec) {
1036 if (ec)
1037 {
1038 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
1039 messages::internalError(asyncResp->res);
1040 return;
1041 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001042
Ed Tanous002d39b2022-05-31 08:59:27 -07001043 messages::success(asyncResp->res);
1044 },
1045 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1046 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1047 "replace");
1048 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001049}
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001050
Jason M. Billsac992cd2022-06-24 13:31:46 -07001051enum class LogParseError
1052{
1053 success,
1054 parseFailed,
1055 messageIdNotInRegistry,
1056};
1057
1058static LogParseError
1059 fillEventLogEntryJson(const std::string& logEntryID,
1060 const std::string& logEntry,
1061 nlohmann::json::object_t& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001062{
Jason M. Bills95820182019-04-22 16:25:34 -07001063 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -07001064 // First get the Timestamp
Ed Tanousf23b7292020-10-15 09:41:17 -07001065 size_t space = logEntry.find_first_of(' ');
Jason M. Billscd225da2019-05-08 15:31:57 -07001066 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -07001067 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001068 return LogParseError::parseFailed;
Jason M. Bills95820182019-04-22 16:25:34 -07001069 }
Jason M. Billscd225da2019-05-08 15:31:57 -07001070 std::string timestamp = logEntry.substr(0, space);
1071 // Then get the log contents
Ed Tanousf23b7292020-10-15 09:41:17 -07001072 size_t entryStart = logEntry.find_first_not_of(' ', space);
Jason M. Billscd225da2019-05-08 15:31:57 -07001073 if (entryStart == std::string::npos)
1074 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001075 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001076 }
1077 std::string_view entry(logEntry);
1078 entry.remove_prefix(entryStart);
1079 // Use split to separate the entry into its fields
1080 std::vector<std::string> logEntryFields;
1081 boost::split(logEntryFields, entry, boost::is_any_of(","),
1082 boost::token_compress_on);
1083 // We need at least a MessageId to be valid
Ed Tanous26f69762022-01-25 09:49:11 -08001084 if (logEntryFields.empty())
Jason M. Billscd225da2019-05-08 15:31:57 -07001085 {
Jason M. Billsac992cd2022-06-24 13:31:46 -07001086 return LogParseError::parseFailed;
Jason M. Billscd225da2019-05-08 15:31:57 -07001087 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001088 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -07001089
Jason M. Bills4851d452019-03-28 11:27:48 -07001090 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08001091 const registries::Message* message = registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001092
Sui Chen54417b02022-03-24 14:59:52 -07001093 if (message == nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001094 {
Sui Chen54417b02022-03-24 14:59:52 -07001095 BMCWEB_LOG_WARNING << "Log entry not found in registry: " << logEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001096 return LogParseError::messageIdNotInRegistry;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001097 }
1098
Sui Chen54417b02022-03-24 14:59:52 -07001099 std::string msg = message->message;
1100
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001101 // Get the MessageArgs from the log if there are any
Ed Tanous26702d02021-11-03 15:02:33 -07001102 std::span<std::string> messageArgs;
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001103 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -07001104 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001105 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001106 // If the first string is empty, assume there are no MessageArgs
1107 std::size_t messageArgsSize = 0;
1108 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -07001109 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001110 messageArgsSize = logEntryFields.size() - 1;
1111 }
1112
Ed Tanous23a21a12020-07-25 04:45:05 +00001113 messageArgs = {&messageArgsStart, messageArgsSize};
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001114
1115 // Fill the MessageArgs into the Message
1116 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001117 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -07001118 {
1119 std::string argStr = "%" + std::to_string(++i);
1120 size_t argPos = msg.find(argStr);
1121 if (argPos != std::string::npos)
1122 {
1123 msg.replace(argPos, argStr.length(), messageArg);
1124 }
Jason M. Bills4851d452019-03-28 11:27:48 -07001125 }
1126 }
1127
Jason M. Bills95820182019-04-22 16:25:34 -07001128 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1129 // format which matches the Redfish format except for the fractional seconds
1130 // between the '.' and the '+', so just remove them.
Ed Tanousf23b7292020-10-15 09:41:17 -07001131 std::size_t dot = timestamp.find_first_of('.');
1132 std::size_t plus = timestamp.find_first_of('+');
Jason M. Bills95820182019-04-22 16:25:34 -07001133 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001134 {
Jason M. Bills95820182019-04-22 16:25:34 -07001135 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001136 }
1137
1138 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07001139 logEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1140 logEntryJson["@odata.id"] =
1141 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" + logEntryID;
1142 logEntryJson["Name"] = "System Event Log Entry";
1143 logEntryJson["Id"] = logEntryID;
1144 logEntryJson["Message"] = std::move(msg);
1145 logEntryJson["MessageId"] = std::move(messageID);
1146 logEntryJson["MessageArgs"] = messageArgs;
1147 logEntryJson["EntryType"] = "Event";
1148 logEntryJson["Severity"] = message->messageSeverity;
1149 logEntryJson["Created"] = std::move(timestamp);
Jason M. Billsac992cd2022-06-24 13:31:46 -07001150 return LogParseError::success;
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001151}
1152
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001153inline void requestRoutesJournalEventLogEntryCollection(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001154{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001155 BMCWEB_ROUTE(app,
1156 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Gunnar Mills8b6a35f2021-07-30 14:52:53 -05001157 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001158 .methods(boost::beast::http::verb::get)(
1159 [&app](const crow::Request& req,
1160 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1161 query_param::QueryCapabilities capabilities = {
1162 .canDelegateTop = true,
1163 .canDelegateSkip = true,
1164 };
1165 query_param::Query delegatedQuery;
1166 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001167 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001168 {
1169 return;
1170 }
1171 // Collections don't include the static data added by SubRoute
1172 // because it has a duplicate entry for members
1173 asyncResp->res.jsonValue["@odata.type"] =
1174 "#LogEntryCollection.LogEntryCollection";
1175 asyncResp->res.jsonValue["@odata.id"] =
1176 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1177 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1178 asyncResp->res.jsonValue["Description"] =
1179 "Collection of System Event Log Entries";
1180
1181 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1182 logEntryArray = nlohmann::json::array();
1183 // Go through the log files and create a unique ID for each
1184 // entry
1185 std::vector<std::filesystem::path> redfishLogFiles;
1186 getRedfishLogFiles(redfishLogFiles);
1187 uint64_t entryCount = 0;
1188 std::string logEntry;
1189
1190 // Oldest logs are in the last file, so start there and loop
1191 // backwards
1192 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1193 it++)
1194 {
1195 std::ifstream logStream(*it);
1196 if (!logStream.is_open())
Jason M. Bills4978b632022-02-22 14:17:43 -08001197 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001198 continue;
Jason M. Bills4978b632022-02-22 14:17:43 -08001199 }
Jason M. Bills897967d2019-07-29 17:05:30 -07001200
Ed Tanous002d39b2022-05-31 08:59:27 -07001201 // Reset the unique ID on the first entry
1202 bool firstEntry = true;
1203 while (std::getline(logStream, logEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001204 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001205 std::string idStr;
1206 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills4978b632022-02-22 14:17:43 -08001207 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001208 continue;
1209 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001210 firstEntry = false;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001211
Jason M. Billsde703c52022-06-23 14:19:04 -07001212 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001213 LogParseError status =
1214 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1215 if (status == LogParseError::messageIdNotInRegistry)
1216 {
1217 continue;
1218 }
1219 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001220 {
1221 messages::internalError(asyncResp->res);
1222 return;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001223 }
Jason M. Billsde703c52022-06-23 14:19:04 -07001224
Jason M. Billsde703c52022-06-23 14:19:04 -07001225 entryCount++;
1226 // Handle paging using skip (number of entries to skip from the
1227 // start) and top (number of entries to display)
1228 if (entryCount <= delegatedQuery.skip ||
1229 entryCount > delegatedQuery.skip + delegatedQuery.top)
1230 {
1231 continue;
1232 }
1233
1234 logEntryArray.push_back(std::move(bmcLogEntry));
Jason M. Bills4978b632022-02-22 14:17:43 -08001235 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001236 }
1237 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1238 if (delegatedQuery.skip + delegatedQuery.top < entryCount)
1239 {
1240 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1241 "/redfish/v1/Systems/system/LogServices/EventLog/Entries?$skip=" +
1242 std::to_string(delegatedQuery.skip + delegatedQuery.top);
1243 }
Jason M. Bills4978b632022-02-22 14:17:43 -08001244 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001245}
Chicago Duan336e96c2019-07-15 14:22:08 +08001246
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001247inline void requestRoutesJournalEventLogEntry(App& app)
1248{
1249 BMCWEB_ROUTE(
1250 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001251 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001252 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001253 [&app](const crow::Request& req,
1254 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1255 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001256 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001257 {
1258 return;
1259 }
1260 const std::string& targetID = param;
1261
1262 // Go through the log files and check the unique ID for each
1263 // entry to find the target entry
1264 std::vector<std::filesystem::path> redfishLogFiles;
1265 getRedfishLogFiles(redfishLogFiles);
1266 std::string logEntry;
1267
1268 // Oldest logs are in the last file, so start there and loop
1269 // backwards
1270 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
1271 it++)
1272 {
1273 std::ifstream logStream(*it);
1274 if (!logStream.is_open())
1275 {
1276 continue;
1277 }
1278
1279 // Reset the unique ID on the first entry
1280 bool firstEntry = true;
1281 while (std::getline(logStream, logEntry))
1282 {
1283 std::string idStr;
1284 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Ed Tanous45ca1b82022-03-25 13:07:27 -07001285 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001286 continue;
1287 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07001288 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07001289
1290 if (idStr == targetID)
1291 {
Jason M. Billsde703c52022-06-23 14:19:04 -07001292 nlohmann::json::object_t bmcLogEntry;
Jason M. Billsac992cd2022-06-24 13:31:46 -07001293 LogParseError status =
1294 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1295 if (status != LogParseError::success)
Ed Tanous002d39b2022-05-31 08:59:27 -07001296 {
1297 messages::internalError(asyncResp->res);
1298 return;
1299 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07001300 asyncResp->res.jsonValue.update(bmcLogEntry);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001301 return;
1302 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001303 }
1304 }
1305 // Requested ID was not found
1306 messages::resourceMissingAtURI(asyncResp->res,
1307 crow::utility::urlFromPieces(targetID));
1308 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001309}
1310
1311inline void requestRoutesDBusEventLogEntryCollection(App& app)
1312{
1313 BMCWEB_ROUTE(app,
1314 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07001315 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07001316 .methods(boost::beast::http::verb::get)(
1317 [&app](const crow::Request& req,
1318 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001319 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001320 {
1321 return;
1322 }
1323 // Collections don't include the static data added by SubRoute
1324 // because it has a duplicate entry for members
1325 asyncResp->res.jsonValue["@odata.type"] =
1326 "#LogEntryCollection.LogEntryCollection";
1327 asyncResp->res.jsonValue["@odata.id"] =
1328 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
1329 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1330 asyncResp->res.jsonValue["Description"] =
1331 "Collection of System Event Log Entries";
1332
1333 // DBus implementation of EventLog/Entries
1334 // Make call to Logging Service to find all log entry objects
1335 crow::connections::systemBus->async_method_call(
1336 [asyncResp](const boost::system::error_code ec,
1337 const dbus::utility::ManagedObjectType& resp) {
1338 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001339 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001340 // TODO Handle for specific error code
1341 BMCWEB_LOG_ERROR
1342 << "getLogEntriesIfaceData resp_handler got error " << ec;
1343 messages::internalError(asyncResp->res);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001344 return;
1345 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001346 nlohmann::json& entriesArray = asyncResp->res.jsonValue["Members"];
1347 entriesArray = nlohmann::json::array();
1348 for (const auto& objectPath : resp)
1349 {
1350 const uint32_t* id = nullptr;
1351 const uint64_t* timestamp = nullptr;
1352 const uint64_t* updateTimestamp = nullptr;
1353 const std::string* severity = nullptr;
1354 const std::string* message = nullptr;
1355 const std::string* filePath = nullptr;
1356 bool resolved = false;
1357 for (const auto& interfaceMap : objectPath.second)
1358 {
1359 if (interfaceMap.first ==
1360 "xyz.openbmc_project.Logging.Entry")
Xiaochao Ma75710de2021-01-21 17:56:02 +08001361 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001362 for (const auto& propertyMap : interfaceMap.second)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001363 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001364 if (propertyMap.first == "Id")
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001365 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001366 id = std::get_if<uint32_t>(&propertyMap.second);
1367 }
1368 else if (propertyMap.first == "Timestamp")
1369 {
1370 timestamp =
1371 std::get_if<uint64_t>(&propertyMap.second);
1372 }
1373 else if (propertyMap.first == "UpdateTimestamp")
1374 {
1375 updateTimestamp =
1376 std::get_if<uint64_t>(&propertyMap.second);
1377 }
1378 else if (propertyMap.first == "Severity")
1379 {
1380 severity = std::get_if<std::string>(
1381 &propertyMap.second);
1382 }
1383 else if (propertyMap.first == "Message")
1384 {
1385 message = std::get_if<std::string>(
1386 &propertyMap.second);
1387 }
1388 else if (propertyMap.first == "Resolved")
1389 {
1390 const bool* resolveptr =
1391 std::get_if<bool>(&propertyMap.second);
1392 if (resolveptr == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001393 {
1394 messages::internalError(asyncResp->res);
1395 return;
1396 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001397 resolved = *resolveptr;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001398 }
1399 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001400 if (id == nullptr || message == nullptr ||
Ed Tanous002d39b2022-05-31 08:59:27 -07001401 severity == nullptr)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001402 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001403 messages::internalError(asyncResp->res);
1404 return;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001405 }
1406 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001407 else if (interfaceMap.first ==
1408 "xyz.openbmc_project.Common.FilePath")
1409 {
1410 for (const auto& propertyMap : interfaceMap.second)
1411 {
1412 if (propertyMap.first == "Path")
1413 {
1414 filePath = std::get_if<std::string>(
1415 &propertyMap.second);
1416 }
1417 }
1418 }
1419 }
1420 // Object path without the
1421 // xyz.openbmc_project.Logging.Entry interface, ignore
1422 // and continue.
1423 if (id == nullptr || message == nullptr ||
1424 severity == nullptr || timestamp == nullptr ||
1425 updateTimestamp == nullptr)
1426 {
1427 continue;
1428 }
1429 entriesArray.push_back({});
1430 nlohmann::json& thisEntry = entriesArray.back();
1431 thisEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
1432 thisEntry["@odata.id"] =
1433 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1434 std::to_string(*id);
1435 thisEntry["Name"] = "System Event Log Entry";
1436 thisEntry["Id"] = std::to_string(*id);
1437 thisEntry["Message"] = *message;
1438 thisEntry["Resolved"] = resolved;
1439 thisEntry["EntryType"] = "Event";
1440 thisEntry["Severity"] =
1441 translateSeverityDbusToRedfish(*severity);
1442 thisEntry["Created"] =
1443 crow::utility::getDateTimeUintMs(*timestamp);
1444 thisEntry["Modified"] =
1445 crow::utility::getDateTimeUintMs(*updateTimestamp);
1446 if (filePath != nullptr)
1447 {
1448 thisEntry["AdditionalDataURI"] =
1449 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1450 std::to_string(*id) + "/attachment";
1451 }
1452 }
1453 std::sort(
1454 entriesArray.begin(), entriesArray.end(),
1455 [](const nlohmann::json& left, const nlohmann::json& right) {
1456 return (left["Id"] <= right["Id"]);
1457 });
1458 asyncResp->res.jsonValue["Members@odata.count"] =
1459 entriesArray.size();
1460 },
1461 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1462 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001463 });
1464}
Xiaochao Ma75710de2021-01-21 17:56:02 +08001465
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001466inline void requestRoutesDBusEventLogEntry(App& app)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001467{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001468 BMCWEB_ROUTE(
1469 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001470 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001471 .methods(boost::beast::http::verb::get)(
1472 [&app](const crow::Request& req,
1473 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1474 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001475 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001476 {
1477 return;
1478 }
1479 std::string entryID = param;
1480 dbus::utility::escapePathForDbus(entryID);
1481
1482 // DBus implementation of EventLog/Entries
1483 // Make call to Logging Service to find all log entry objects
1484 crow::connections::systemBus->async_method_call(
1485 [asyncResp, entryID](const boost::system::error_code ec,
1486 const dbus::utility::DBusPropertiesMap& resp) {
1487 if (ec.value() == EBADR)
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001488 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001489 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1490 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001491 return;
1492 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001493 if (ec)
1494 {
1495 BMCWEB_LOG_ERROR
1496 << "EventLogEntry (DBus) resp_handler got error " << ec;
1497 messages::internalError(asyncResp->res);
1498 return;
1499 }
1500 const uint32_t* id = nullptr;
1501 const uint64_t* timestamp = nullptr;
1502 const uint64_t* updateTimestamp = nullptr;
1503 const std::string* severity = nullptr;
1504 const std::string* message = nullptr;
1505 const std::string* filePath = nullptr;
1506 bool resolved = false;
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001507
Ed Tanous002d39b2022-05-31 08:59:27 -07001508 for (const auto& propertyMap : resp)
1509 {
1510 if (propertyMap.first == "Id")
1511 {
1512 id = std::get_if<uint32_t>(&propertyMap.second);
1513 }
1514 else if (propertyMap.first == "Timestamp")
1515 {
1516 timestamp = std::get_if<uint64_t>(&propertyMap.second);
1517 }
1518 else if (propertyMap.first == "UpdateTimestamp")
1519 {
1520 updateTimestamp =
1521 std::get_if<uint64_t>(&propertyMap.second);
1522 }
1523 else if (propertyMap.first == "Severity")
1524 {
1525 severity = std::get_if<std::string>(&propertyMap.second);
1526 }
1527 else if (propertyMap.first == "Message")
1528 {
1529 message = std::get_if<std::string>(&propertyMap.second);
1530 }
1531 else if (propertyMap.first == "Resolved")
1532 {
1533 const bool* resolveptr =
1534 std::get_if<bool>(&propertyMap.second);
1535 if (resolveptr == nullptr)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001536 {
1537 messages::internalError(asyncResp->res);
1538 return;
1539 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001540 resolved = *resolveptr;
1541 }
1542 else if (propertyMap.first == "Path")
1543 {
1544 filePath = std::get_if<std::string>(&propertyMap.second);
1545 }
1546 }
1547 if (id == nullptr || message == nullptr || severity == nullptr ||
1548 timestamp == nullptr || updateTimestamp == nullptr)
1549 {
1550 messages::internalError(asyncResp->res);
1551 return;
1552 }
1553 asyncResp->res.jsonValue["@odata.type"] =
1554 "#LogEntry.v1_8_0.LogEntry";
1555 asyncResp->res.jsonValue["@odata.id"] =
1556 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1557 std::to_string(*id);
1558 asyncResp->res.jsonValue["Name"] = "System Event Log Entry";
1559 asyncResp->res.jsonValue["Id"] = std::to_string(*id);
1560 asyncResp->res.jsonValue["Message"] = *message;
1561 asyncResp->res.jsonValue["Resolved"] = resolved;
1562 asyncResp->res.jsonValue["EntryType"] = "Event";
1563 asyncResp->res.jsonValue["Severity"] =
1564 translateSeverityDbusToRedfish(*severity);
1565 asyncResp->res.jsonValue["Created"] =
1566 crow::utility::getDateTimeUintMs(*timestamp);
1567 asyncResp->res.jsonValue["Modified"] =
1568 crow::utility::getDateTimeUintMs(*updateTimestamp);
1569 if (filePath != nullptr)
1570 {
1571 asyncResp->res.jsonValue["AdditionalDataURI"] =
1572 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
1573 std::to_string(*id) + "/attachment";
1574 }
1575 },
1576 "xyz.openbmc_project.Logging",
1577 "/xyz/openbmc_project/logging/entry/" + entryID,
1578 "org.freedesktop.DBus.Properties", "GetAll", "");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001579 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001580
1581 BMCWEB_ROUTE(
1582 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001583 .privileges(redfish::privileges::patchLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001584 .methods(boost::beast::http::verb::patch)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001585 [&app](const crow::Request& req,
1586 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1587 const std::string& entryId) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001588 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001589 {
1590 return;
1591 }
1592 std::optional<bool> resolved;
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001593
Ed Tanous002d39b2022-05-31 08:59:27 -07001594 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved",
1595 resolved))
1596 {
1597 return;
1598 }
1599 BMCWEB_LOG_DEBUG << "Set Resolved";
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001600
Ed Tanous002d39b2022-05-31 08:59:27 -07001601 crow::connections::systemBus->async_method_call(
1602 [asyncResp, entryId](const boost::system::error_code ec) {
1603 if (ec)
1604 {
1605 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1606 messages::internalError(asyncResp->res);
1607 return;
1608 }
1609 },
1610 "xyz.openbmc_project.Logging",
1611 "/xyz/openbmc_project/logging/entry/" + entryId,
1612 "org.freedesktop.DBus.Properties", "Set",
1613 "xyz.openbmc_project.Logging.Entry", "Resolved",
1614 dbus::utility::DbusVariantType(*resolved));
1615 });
Adriana Kobylak400fd1f2021-01-29 09:01:30 -06001616
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001617 BMCWEB_ROUTE(
1618 app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07001619 .privileges(redfish::privileges::deleteLogEntry)
1620
Ed Tanous002d39b2022-05-31 08:59:27 -07001621 .methods(boost::beast::http::verb::delete_)(
1622 [&app](const crow::Request& req,
1623 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1624 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001625 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001626 {
1627 return;
1628 }
1629 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1630
1631 std::string entryID = param;
1632
1633 dbus::utility::escapePathForDbus(entryID);
1634
1635 // Process response from Logging service.
1636 auto respHandler =
1637 [asyncResp, entryID](const boost::system::error_code ec) {
1638 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1639 if (ec)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001640 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001641 if (ec.value() == EBADR)
Ed Tanous45ca1b82022-03-25 13:07:27 -07001642 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001643 messages::resourceNotFound(asyncResp->res, "LogEntry",
1644 entryID);
Ed Tanous45ca1b82022-03-25 13:07:27 -07001645 return;
1646 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001647 // TODO Handle for specific error code
1648 BMCWEB_LOG_ERROR
1649 << "EventLogEntry (DBus) doDelete respHandler got error "
1650 << ec;
1651 asyncResp->res.result(
1652 boost::beast::http::status::internal_server_error);
1653 return;
1654 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001655
Ed Tanous002d39b2022-05-31 08:59:27 -07001656 asyncResp->res.result(boost::beast::http::status::ok);
1657 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001658
Ed Tanous002d39b2022-05-31 08:59:27 -07001659 // Make call to Logging service to request Delete Log
1660 crow::connections::systemBus->async_method_call(
1661 respHandler, "xyz.openbmc_project.Logging",
1662 "/xyz/openbmc_project/logging/entry/" + entryID,
1663 "xyz.openbmc_project.Object.Delete", "Delete");
Ed Tanous45ca1b82022-03-25 13:07:27 -07001664 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001665}
1666
1667inline void requestRoutesDBusEventLogEntryDownload(App& app)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001668{
George Liu0fda0f12021-11-16 10:06:17 +08001669 BMCWEB_ROUTE(
1670 app,
1671 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/attachment")
Ed Tanoused398212021-06-09 17:05:54 -07001672 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001673 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001674 [&app](const crow::Request& req,
1675 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1676 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001677 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001678 {
1679 return;
1680 }
1681 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
1682 {
1683 asyncResp->res.result(boost::beast::http::status::bad_request);
1684 return;
1685 }
zhanghch058d1b46d2021-04-01 11:18:24 +08001686
Ed Tanous002d39b2022-05-31 08:59:27 -07001687 std::string entryID = param;
1688 dbus::utility::escapePathForDbus(entryID);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001689
Ed Tanous002d39b2022-05-31 08:59:27 -07001690 crow::connections::systemBus->async_method_call(
1691 [asyncResp, entryID](const boost::system::error_code ec,
1692 const sdbusplus::message::unix_fd& unixfd) {
1693 if (ec.value() == EBADR)
1694 {
1695 messages::resourceNotFound(asyncResp->res, "EventLogAttachment",
1696 entryID);
1697 return;
1698 }
1699 if (ec)
1700 {
1701 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
1702 messages::internalError(asyncResp->res);
1703 return;
1704 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001705
Ed Tanous002d39b2022-05-31 08:59:27 -07001706 int fd = -1;
1707 fd = dup(unixfd);
1708 if (fd == -1)
1709 {
1710 messages::internalError(asyncResp->res);
1711 return;
1712 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001713
Ed Tanous002d39b2022-05-31 08:59:27 -07001714 long long int size = lseek(fd, 0, SEEK_END);
1715 if (size == -1)
1716 {
1717 messages::internalError(asyncResp->res);
1718 return;
1719 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001720
Ed Tanous002d39b2022-05-31 08:59:27 -07001721 // Arbitrary max size of 64kb
1722 constexpr int maxFileSize = 65536;
1723 if (size > maxFileSize)
1724 {
1725 BMCWEB_LOG_ERROR << "File size exceeds maximum allowed size of "
1726 << maxFileSize;
1727 messages::internalError(asyncResp->res);
1728 return;
1729 }
1730 std::vector<char> data(static_cast<size_t>(size));
1731 long long int rc = lseek(fd, 0, SEEK_SET);
1732 if (rc == -1)
1733 {
1734 messages::internalError(asyncResp->res);
1735 return;
1736 }
1737 rc = read(fd, data.data(), data.size());
1738 if ((rc == -1) || (rc != size))
1739 {
1740 messages::internalError(asyncResp->res);
1741 return;
1742 }
1743 close(fd);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001744
Ed Tanous002d39b2022-05-31 08:59:27 -07001745 std::string_view strData(data.data(), data.size());
1746 std::string output = crow::utility::base64encode(strData);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001747
Ed Tanous002d39b2022-05-31 08:59:27 -07001748 asyncResp->res.addHeader("Content-Type",
1749 "application/octet-stream");
1750 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
1751 asyncResp->res.body() = std::move(output);
1752 },
1753 "xyz.openbmc_project.Logging",
1754 "/xyz/openbmc_project/logging/entry/" + entryID,
1755 "xyz.openbmc_project.Logging.Entry", "GetEntry");
1756 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07001757}
1758
Spencer Kub7028eb2021-10-26 15:27:35 +08001759constexpr const char* hostLoggerFolderPath = "/var/log/console";
1760
1761inline bool
1762 getHostLoggerFiles(const std::string& hostLoggerFilePath,
1763 std::vector<std::filesystem::path>& hostLoggerFiles)
1764{
1765 std::error_code ec;
1766 std::filesystem::directory_iterator logPath(hostLoggerFilePath, ec);
1767 if (ec)
1768 {
1769 BMCWEB_LOG_ERROR << ec.message();
1770 return false;
1771 }
1772 for (const std::filesystem::directory_entry& it : logPath)
1773 {
1774 std::string filename = it.path().filename();
1775 // Prefix of each log files is "log". Find the file and save the
1776 // path
1777 if (boost::starts_with(filename, "log"))
1778 {
1779 hostLoggerFiles.emplace_back(it.path());
1780 }
1781 }
1782 // As the log files rotate, they are appended with a ".#" that is higher for
1783 // the older logs. Since we start from oldest logs, sort the name in
1784 // descending order.
1785 std::sort(hostLoggerFiles.rbegin(), hostLoggerFiles.rend(),
1786 AlphanumLess<std::string>());
1787
1788 return true;
1789}
1790
Ed Tanous02cad962022-06-30 16:50:15 -07001791inline bool getHostLoggerEntries(
1792 const std::vector<std::filesystem::path>& hostLoggerFiles, uint64_t skip,
1793 uint64_t top, std::vector<std::string>& logEntries, size_t& logCount)
Spencer Kub7028eb2021-10-26 15:27:35 +08001794{
1795 GzFileReader logFile;
1796
1797 // Go though all log files and expose host logs.
1798 for (const std::filesystem::path& it : hostLoggerFiles)
1799 {
1800 if (!logFile.gzGetLines(it.string(), skip, top, logEntries, logCount))
1801 {
1802 BMCWEB_LOG_ERROR << "fail to expose host logs";
1803 return false;
1804 }
1805 }
1806 // Get lastMessage from constructor by getter
1807 std::string lastMessage = logFile.getLastMessage();
1808 if (!lastMessage.empty())
1809 {
1810 logCount++;
1811 if (logCount > skip && logCount <= (skip + top))
1812 {
1813 logEntries.push_back(lastMessage);
1814 }
1815 }
1816 return true;
1817}
1818
1819inline void fillHostLoggerEntryJson(const std::string& logEntryID,
1820 const std::string& msg,
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001821 nlohmann::json::object_t& logEntryJson)
Spencer Kub7028eb2021-10-26 15:27:35 +08001822{
1823 // Fill in the log entry with the gathered data.
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001824 logEntryJson["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
1825 logEntryJson["@odata.id"] =
1826 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/" +
1827 logEntryID;
1828 logEntryJson["Name"] = "Host Logger Entry";
1829 logEntryJson["Id"] = logEntryID;
1830 logEntryJson["Message"] = msg;
1831 logEntryJson["EntryType"] = "Oem";
1832 logEntryJson["Severity"] = "OK";
1833 logEntryJson["OemRecordFormat"] = "Host Logger Entry";
Spencer Kub7028eb2021-10-26 15:27:35 +08001834}
1835
1836inline void requestRoutesSystemHostLogger(App& app)
1837{
1838 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/HostLogger/")
1839 .privileges(redfish::privileges::getLogService)
Ed Tanous14766872022-03-15 10:44:42 -07001840 .methods(boost::beast::http::verb::get)(
1841 [&app](const crow::Request& req,
1842 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001843 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001844 {
1845 return;
1846 }
1847 asyncResp->res.jsonValue["@odata.id"] =
1848 "/redfish/v1/Systems/system/LogServices/HostLogger";
1849 asyncResp->res.jsonValue["@odata.type"] =
1850 "#LogService.v1_1_0.LogService";
1851 asyncResp->res.jsonValue["Name"] = "Host Logger Service";
1852 asyncResp->res.jsonValue["Description"] = "Host Logger Service";
1853 asyncResp->res.jsonValue["Id"] = "HostLogger";
1854 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
1855 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1856 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001857}
1858
1859inline void requestRoutesSystemHostLoggerCollection(App& app)
1860{
1861 BMCWEB_ROUTE(app,
1862 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/")
1863 .privileges(redfish::privileges::getLogEntry)
Ed Tanous002d39b2022-05-31 08:59:27 -07001864 .methods(boost::beast::http::verb::get)(
1865 [&app](const crow::Request& req,
1866 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
1867 query_param::QueryCapabilities capabilities = {
1868 .canDelegateTop = true,
1869 .canDelegateSkip = true,
1870 };
1871 query_param::Query delegatedQuery;
1872 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00001873 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07001874 {
1875 return;
1876 }
1877 asyncResp->res.jsonValue["@odata.id"] =
1878 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries";
1879 asyncResp->res.jsonValue["@odata.type"] =
1880 "#LogEntryCollection.LogEntryCollection";
1881 asyncResp->res.jsonValue["Name"] = "HostLogger Entries";
1882 asyncResp->res.jsonValue["Description"] =
1883 "Collection of HostLogger Entries";
1884 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1885 logEntryArray = nlohmann::json::array();
1886 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Spencer Kub7028eb2021-10-26 15:27:35 +08001887
Ed Tanous002d39b2022-05-31 08:59:27 -07001888 std::vector<std::filesystem::path> hostLoggerFiles;
1889 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1890 {
1891 BMCWEB_LOG_ERROR << "fail to get host log file path";
1892 return;
1893 }
1894
1895 size_t logCount = 0;
1896 // This vector only store the entries we want to expose that
1897 // control by skip and top.
1898 std::vector<std::string> logEntries;
1899 if (!getHostLoggerEntries(hostLoggerFiles, delegatedQuery.skip,
1900 delegatedQuery.top, logEntries, logCount))
1901 {
1902 messages::internalError(asyncResp->res);
1903 return;
1904 }
1905 // If vector is empty, that means skip value larger than total
1906 // log count
1907 if (logEntries.empty())
1908 {
1909 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1910 return;
1911 }
1912 if (!logEntries.empty())
1913 {
1914 for (size_t i = 0; i < logEntries.size(); i++)
George Liu0fda0f12021-11-16 10:06:17 +08001915 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001916 nlohmann::json::object_t hostLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07001917 fillHostLoggerEntryJson(std::to_string(delegatedQuery.skip + i),
1918 logEntries[i], hostLogEntry);
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001919 logEntryArray.push_back(std::move(hostLogEntry));
George Liu0fda0f12021-11-16 10:06:17 +08001920 }
1921
Ed Tanous002d39b2022-05-31 08:59:27 -07001922 asyncResp->res.jsonValue["Members@odata.count"] = logCount;
1923 if (delegatedQuery.skip + delegatedQuery.top < logCount)
George Liu0fda0f12021-11-16 10:06:17 +08001924 {
Ed Tanous002d39b2022-05-31 08:59:27 -07001925 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1926 "/redfish/v1/Systems/system/LogServices/HostLogger/Entries?$skip=" +
1927 std::to_string(delegatedQuery.skip + delegatedQuery.top);
George Liu0fda0f12021-11-16 10:06:17 +08001928 }
Ed Tanous002d39b2022-05-31 08:59:27 -07001929 }
George Liu0fda0f12021-11-16 10:06:17 +08001930 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001931}
1932
1933inline void requestRoutesSystemHostLoggerLogEntry(App& app)
1934{
1935 BMCWEB_ROUTE(
1936 app, "/redfish/v1/Systems/system/LogServices/HostLogger/Entries/<str>/")
1937 .privileges(redfish::privileges::getLogEntry)
1938 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07001939 [&app](const crow::Request& req,
1940 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1941 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00001942 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07001943 {
1944 return;
1945 }
1946 const std::string& targetID = param;
Spencer Kub7028eb2021-10-26 15:27:35 +08001947
Ed Tanous002d39b2022-05-31 08:59:27 -07001948 uint64_t idInt = 0;
Ed Tanousca45aa32022-01-07 09:28:45 -08001949
Ed Tanous002d39b2022-05-31 08:59:27 -07001950 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
1951 const char* end = targetID.data() + targetID.size();
Ed Tanousca45aa32022-01-07 09:28:45 -08001952
Ed Tanous002d39b2022-05-31 08:59:27 -07001953 auto [ptr, ec] = std::from_chars(targetID.data(), end, idInt);
1954 if (ec == std::errc::invalid_argument)
1955 {
1956 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1957 return;
1958 }
1959 if (ec == std::errc::result_out_of_range)
1960 {
1961 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1962 return;
1963 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001964
Ed Tanous002d39b2022-05-31 08:59:27 -07001965 std::vector<std::filesystem::path> hostLoggerFiles;
1966 if (!getHostLoggerFiles(hostLoggerFolderPath, hostLoggerFiles))
1967 {
1968 BMCWEB_LOG_ERROR << "fail to get host log file path";
1969 return;
1970 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001971
Ed Tanous002d39b2022-05-31 08:59:27 -07001972 size_t logCount = 0;
1973 uint64_t top = 1;
1974 std::vector<std::string> logEntries;
1975 // We can get specific entry by skip and top. For example, if we
1976 // want to get nth entry, we can set skip = n-1 and top = 1 to
1977 // get that entry
1978 if (!getHostLoggerEntries(hostLoggerFiles, idInt, top, logEntries,
1979 logCount))
1980 {
1981 messages::internalError(asyncResp->res);
1982 return;
1983 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001984
Ed Tanous002d39b2022-05-31 08:59:27 -07001985 if (!logEntries.empty())
1986 {
Jason M. Bills6d6574c2022-06-28 12:30:16 -07001987 nlohmann::json::object_t hostLogEntry;
1988 fillHostLoggerEntryJson(targetID, logEntries[0], hostLogEntry);
1989 asyncResp->res.jsonValue.update(hostLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07001990 return;
1991 }
Spencer Kub7028eb2021-10-26 15:27:35 +08001992
Ed Tanous002d39b2022-05-31 08:59:27 -07001993 // Requested ID was not found
1994 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
1995 });
Spencer Kub7028eb2021-10-26 15:27:35 +08001996}
1997
Claire Weinanfdd26902022-03-01 14:18:25 -08001998constexpr char const* dumpManagerIface =
1999 "xyz.openbmc_project.Collection.DeleteAll";
2000inline void handleLogServicesCollectionGet(
2001 crow::App& app, const crow::Request& req,
2002 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2003{
2004 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2005 {
2006 return;
2007 }
2008 // Collections don't include the static data added by SubRoute
2009 // because it has a duplicate entry for members
2010 asyncResp->res.jsonValue["@odata.type"] =
2011 "#LogServiceCollection.LogServiceCollection";
2012 asyncResp->res.jsonValue["@odata.id"] =
2013 "/redfish/v1/Managers/bmc/LogServices";
2014 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
2015 asyncResp->res.jsonValue["Description"] =
2016 "Collection of LogServices for this Manager";
2017 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
2018 logServiceArray = nlohmann::json::array();
2019
2020#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
2021 logServiceArray.push_back(
2022 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
2023#endif
2024
2025 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
2026
2027#ifdef BMCWEB_ENABLE_REDFISH_DUMP_LOG
2028 auto respHandler =
2029 [asyncResp](
2030 const boost::system::error_code ec,
2031 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2032 if (ec)
2033 {
2034 BMCWEB_LOG_ERROR
2035 << "handleLogServicesCollectionGet respHandler got error "
2036 << ec;
2037 // Assume that getting an error simply means there are no dump
2038 // LogServices. Return without adding any error response.
2039 return;
2040 }
2041
2042 nlohmann::json& logServiceArrayLocal =
2043 asyncResp->res.jsonValue["Members"];
2044
2045 for (const std::string& path : subTreePaths)
2046 {
2047 if (path == "/xyz/openbmc_project/dump/bmc")
2048 {
2049 logServiceArrayLocal.push_back(
2050 {{"@odata.id",
2051 "/redfish/v1/Managers/bmc/LogServices/Dump"}});
2052 }
2053 else if (path == "/xyz/openbmc_project/dump/faultlog")
2054 {
2055 logServiceArrayLocal.push_back(
2056 {{"@odata.id",
2057 "/redfish/v1/Managers/bmc/LogServices/FaultLog"}});
2058 }
2059 }
2060
2061 asyncResp->res.jsonValue["Members@odata.count"] =
2062 logServiceArrayLocal.size();
2063 };
2064
2065 crow::connections::systemBus->async_method_call(
2066 respHandler, "xyz.openbmc_project.ObjectMapper",
2067 "/xyz/openbmc_project/object_mapper",
2068 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
2069 "/xyz/openbmc_project/dump", 0,
2070 std::array<const char*, 1>{dumpManagerIface});
2071#endif
2072}
2073
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002074inline void requestRoutesBMCLogServiceCollection(App& app)
2075{
2076 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/")
Gunnar Millsad89dcf2021-07-30 14:40:11 -05002077 .privileges(redfish::privileges::getLogServiceCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002078 .methods(boost::beast::http::verb::get)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002079 std::bind_front(handleLogServicesCollectionGet, std::ref(app)));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002080}
Ed Tanous1da66f72018-07-27 16:13:37 -07002081
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002082inline void requestRoutesBMCJournalLogService(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002083{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002084 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Ed Tanoused398212021-06-09 17:05:54 -07002085 .privileges(redfish::privileges::getLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002086 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002087 [&app](const crow::Request& req,
2088 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002089 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002090 {
2091 return;
2092 }
2093 asyncResp->res.jsonValue["@odata.type"] =
2094 "#LogService.v1_1_0.LogService";
2095 asyncResp->res.jsonValue["@odata.id"] =
2096 "/redfish/v1/Managers/bmc/LogServices/Journal";
2097 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
2098 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
2099 asyncResp->res.jsonValue["Id"] = "BMC Journal";
2100 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302101
Ed Tanous002d39b2022-05-31 08:59:27 -07002102 std::pair<std::string, std::string> redfishDateTimeOffset =
2103 crow::utility::getDateTimeOffsetNow();
2104 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2105 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2106 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302107
Ed Tanous002d39b2022-05-31 08:59:27 -07002108 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2109 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2110 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002111}
Jason M. Billse1f26342018-07-18 12:12:00 -07002112
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002113static int
2114 fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
2115 sd_journal* journal,
2116 nlohmann::json::object_t& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07002117{
2118 // Get the Log Entry contents
2119 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07002120
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002121 std::string message;
2122 std::string_view syslogID;
2123 ret = getJournalMetadata(journal, "SYSLOG_IDENTIFIER", syslogID);
2124 if (ret < 0)
2125 {
2126 BMCWEB_LOG_ERROR << "Failed to read SYSLOG_IDENTIFIER field: "
2127 << strerror(-ret);
2128 }
2129 if (!syslogID.empty())
2130 {
2131 message += std::string(syslogID) + ": ";
2132 }
2133
Ed Tanous39e77502019-03-04 17:35:53 -08002134 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07002135 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002136 if (ret < 0)
2137 {
2138 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
2139 return 1;
2140 }
Jason M. Billsa8fe54f2020-11-20 15:57:55 -08002141 message += std::string(msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07002142
2143 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07002144 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07002145 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07002146 if (ret < 0)
2147 {
2148 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07002149 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002150
2151 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07002152 std::string entryTimeStr;
2153 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07002154 {
Jason M. Bills16428a12018-11-02 12:42:29 -07002155 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07002156 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002157
2158 // Fill in the log entry with the gathered data
Jason M. Bills84afc482022-06-24 12:38:23 -07002159 bmcJournalLogEntryJson["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
2160 bmcJournalLogEntryJson["@odata.id"] =
2161 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
2162 bmcJournalLogEntryID;
2163 bmcJournalLogEntryJson["Name"] = "BMC Journal Entry";
2164 bmcJournalLogEntryJson["Id"] = bmcJournalLogEntryID;
2165 bmcJournalLogEntryJson["Message"] = std::move(message);
2166 bmcJournalLogEntryJson["EntryType"] = "Oem";
2167 bmcJournalLogEntryJson["Severity"] = severity <= 2 ? "Critical"
2168 : severity <= 4 ? "Warning"
2169 : "OK";
2170 bmcJournalLogEntryJson["OemRecordFormat"] = "BMC Journal Entry";
2171 bmcJournalLogEntryJson["Created"] = std::move(entryTimeStr);
Jason M. Billse1f26342018-07-18 12:12:00 -07002172 return 0;
2173}
2174
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002175inline void requestRoutesBMCJournalLogEntryCollection(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002176{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002177 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002178 .privileges(redfish::privileges::getLogEntryCollection)
Ed Tanous002d39b2022-05-31 08:59:27 -07002179 .methods(boost::beast::http::verb::get)(
2180 [&app](const crow::Request& req,
2181 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
2182 query_param::QueryCapabilities capabilities = {
2183 .canDelegateTop = true,
2184 .canDelegateSkip = true,
2185 };
2186 query_param::Query delegatedQuery;
2187 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00002188 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07002189 {
2190 return;
2191 }
2192 // Collections don't include the static data added by SubRoute
2193 // because it has a duplicate entry for members
2194 asyncResp->res.jsonValue["@odata.type"] =
2195 "#LogEntryCollection.LogEntryCollection";
2196 asyncResp->res.jsonValue["@odata.id"] =
2197 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
2198 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
2199 asyncResp->res.jsonValue["Description"] =
2200 "Collection of BMC Journal Entries";
2201 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
2202 logEntryArray = nlohmann::json::array();
Jason M. Billse1f26342018-07-18 12:12:00 -07002203
Ed Tanous002d39b2022-05-31 08:59:27 -07002204 // Go through the journal and use the timestamp to create a
2205 // unique ID for each entry
2206 sd_journal* journalTmp = nullptr;
2207 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2208 if (ret < 0)
2209 {
2210 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2211 messages::internalError(asyncResp->res);
2212 return;
2213 }
2214 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2215 journalTmp, sd_journal_close);
2216 journalTmp = nullptr;
2217 uint64_t entryCount = 0;
2218 // Reset the unique ID on the first entry
2219 bool firstEntry = true;
2220 SD_JOURNAL_FOREACH(journal.get())
2221 {
2222 entryCount++;
2223 // Handle paging using skip (number of entries to skip from
2224 // the start) and top (number of entries to display)
2225 if (entryCount <= delegatedQuery.skip ||
2226 entryCount > delegatedQuery.skip + delegatedQuery.top)
George Liu0fda0f12021-11-16 10:06:17 +08002227 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002228 continue;
2229 }
2230
2231 std::string idStr;
2232 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2233 {
2234 continue;
2235 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002236 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002237
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002238 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002239 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
2240 bmcJournalLogEntry) != 0)
2241 {
George Liu0fda0f12021-11-16 10:06:17 +08002242 messages::internalError(asyncResp->res);
2243 return;
2244 }
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002245 logEntryArray.push_back(std::move(bmcJournalLogEntry));
Ed Tanous002d39b2022-05-31 08:59:27 -07002246 }
2247 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
2248 if (delegatedQuery.skip + delegatedQuery.top < entryCount)
2249 {
2250 asyncResp->res.jsonValue["Members@odata.nextLink"] =
2251 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
2252 std::to_string(delegatedQuery.skip + delegatedQuery.top);
2253 }
George Liu0fda0f12021-11-16 10:06:17 +08002254 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002255}
Jason M. Billse1f26342018-07-18 12:12:00 -07002256
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002257inline void requestRoutesBMCJournalLogEntry(App& app)
Jason M. Billse1f26342018-07-18 12:12:00 -07002258{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002259 BMCWEB_ROUTE(app,
2260 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002261 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002262 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002263 [&app](const crow::Request& req,
2264 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2265 const std::string& entryID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002266 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002267 {
2268 return;
2269 }
2270 // Convert the unique ID back to a timestamp to find the entry
2271 uint64_t ts = 0;
2272 uint64_t index = 0;
2273 if (!getTimestampFromID(asyncResp, entryID, ts, index))
2274 {
2275 return;
2276 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002277
Ed Tanous002d39b2022-05-31 08:59:27 -07002278 sd_journal* journalTmp = nullptr;
2279 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
2280 if (ret < 0)
2281 {
2282 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
2283 messages::internalError(asyncResp->res);
2284 return;
2285 }
2286 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
2287 journalTmp, sd_journal_close);
2288 journalTmp = nullptr;
2289 // Go to the timestamp in the log and move to the entry at the
2290 // index tracking the unique ID
2291 std::string idStr;
2292 bool firstEntry = true;
2293 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
2294 if (ret < 0)
2295 {
2296 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
2297 << strerror(-ret);
2298 messages::internalError(asyncResp->res);
2299 return;
2300 }
2301 for (uint64_t i = 0; i <= index; i++)
2302 {
2303 sd_journal_next(journal.get());
2304 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
2305 {
2306 messages::internalError(asyncResp->res);
2307 return;
2308 }
Jason M. Billsefde4ec2022-06-24 08:59:52 -07002309 firstEntry = false;
Ed Tanous002d39b2022-05-31 08:59:27 -07002310 }
2311 // Confirm that the entry ID matches what was requested
2312 if (idStr != entryID)
2313 {
2314 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
2315 return;
2316 }
zhanghch058d1b46d2021-04-01 11:18:24 +08002317
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002318 nlohmann::json::object_t bmcJournalLogEntry;
Ed Tanous002d39b2022-05-31 08:59:27 -07002319 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
Jason M. Bills3a48b3a2022-06-24 10:10:15 -07002320 bmcJournalLogEntry) != 0)
Ed Tanous002d39b2022-05-31 08:59:27 -07002321 {
2322 messages::internalError(asyncResp->res);
2323 return;
2324 }
Jason M. Billsd405bb52022-06-24 10:52:05 -07002325 asyncResp->res.jsonValue.update(bmcJournalLogEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002326 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002327}
2328
Claire Weinanfdd26902022-03-01 14:18:25 -08002329inline void
2330 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2331 const std::string& dumpType)
2332{
2333 std::string dumpPath;
2334 std::string overWritePolicy;
2335 bool collectDiagnosticDataSupported = false;
2336
2337 if (dumpType == "BMC")
2338 {
2339 dumpPath = "/redfish/v1/Managers/bmc/LogServices/Dump";
2340 overWritePolicy = "WrapsWhenFull";
2341 collectDiagnosticDataSupported = true;
2342 }
2343 else if (dumpType == "FaultLog")
2344 {
2345 dumpPath = "/redfish/v1/Managers/bmc/LogServices/FaultLog";
2346 overWritePolicy = "Unknown";
2347 collectDiagnosticDataSupported = false;
2348 }
2349 else if (dumpType == "System")
2350 {
2351 dumpPath = "/redfish/v1/Systems/system/LogServices/Dump";
2352 overWritePolicy = "WrapsWhenFull";
2353 collectDiagnosticDataSupported = true;
2354 }
2355 else
2356 {
2357 BMCWEB_LOG_ERROR << "getDumpServiceInfo() invalid dump type: "
2358 << dumpType;
2359 messages::internalError(asyncResp->res);
2360 return;
2361 }
2362
2363 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2364 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2365 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2366 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2367 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2368 asyncResp->res.jsonValue["OverWritePolicy"] = std::move(overWritePolicy);
2369
2370 std::pair<std::string, std::string> redfishDateTimeOffset =
2371 crow::utility::getDateTimeOffsetNow();
2372 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2373 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2374 redfishDateTimeOffset.second;
2375
2376 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2377 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2378 dumpPath + "/Actions/LogService.ClearLog";
2379
2380 if (collectDiagnosticDataSupported)
2381 {
2382 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2383 ["target"] =
2384 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2385 }
2386}
2387
2388inline void handleLogServicesDumpServiceGet(
2389 crow::App& app, const std::string& dumpType, const crow::Request& req,
2390 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2391{
2392 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2393 {
2394 return;
2395 }
2396 getDumpServiceInfo(asyncResp, dumpType);
2397}
2398
2399inline void handleLogServicesDumpEntriesCollectionGet(
2400 crow::App& app, const std::string& dumpType, const crow::Request& req,
2401 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2402{
2403 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2404 {
2405 return;
2406 }
2407 getDumpEntryCollection(asyncResp, dumpType);
2408}
2409
2410inline void handleLogServicesDumpEntryGet(
2411 crow::App& app, const std::string& dumpType, const crow::Request& req,
2412 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2413 const std::string& dumpId)
2414{
2415 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2416 {
2417 return;
2418 }
2419 getDumpEntryById(asyncResp, dumpId, dumpType);
2420}
2421
2422inline void handleLogServicesDumpEntryDelete(
2423 crow::App& app, const std::string& dumpType, const crow::Request& req,
2424 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2425 const std::string& dumpId)
2426{
2427 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2428 {
2429 return;
2430 }
2431 deleteDumpEntry(asyncResp, dumpId, dumpType);
2432}
2433
2434inline void handleLogServicesDumpCollectDiagnosticDataPost(
2435 crow::App& app, const std::string& dumpType, const crow::Request& req,
2436 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2437{
2438 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2439 {
2440 return;
2441 }
2442 createDump(asyncResp, req, dumpType);
2443}
2444
2445inline void handleLogServicesDumpClearLogPost(
2446 crow::App& app, const std::string& dumpType, const crow::Request& req,
2447 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
2448{
2449 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2450 {
2451 return;
2452 }
2453 clearDump(asyncResp, dumpType);
2454}
2455
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002456inline void requestRoutesBMCDumpService(App& app)
2457{
2458 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002459 .privileges(redfish::privileges::getLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002460 .methods(boost::beast::http::verb::get)(std::bind_front(
2461 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002462}
2463
2464inline void requestRoutesBMCDumpEntryCollection(App& app)
2465{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002466 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002467 .privileges(redfish::privileges::getLogEntryCollection)
Claire Weinanfdd26902022-03-01 14:18:25 -08002468 .methods(boost::beast::http::verb::get)(std::bind_front(
2469 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002470}
2471
2472inline void requestRoutesBMCDumpEntry(App& app)
2473{
2474 BMCWEB_ROUTE(app,
2475 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002476 .privileges(redfish::privileges::getLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002477 .methods(boost::beast::http::verb::get)(std::bind_front(
2478 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2479
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002480 BMCWEB_ROUTE(app,
2481 "/redfish/v1/Managers/bmc/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002482 .privileges(redfish::privileges::deleteLogEntry)
Claire Weinanfdd26902022-03-01 14:18:25 -08002483 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2484 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002485}
2486
2487inline void requestRoutesBMCDumpCreate(App& app)
2488{
George Liu0fda0f12021-11-16 10:06:17 +08002489 BMCWEB_ROUTE(
2490 app,
2491 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002492 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002493 .methods(boost::beast::http::verb::post)(
Claire Weinanfdd26902022-03-01 14:18:25 -08002494 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2495 std::ref(app), "BMC"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002496}
2497
2498inline void requestRoutesBMCDumpClear(App& app)
2499{
George Liu0fda0f12021-11-16 10:06:17 +08002500 BMCWEB_ROUTE(
2501 app,
2502 "/redfish/v1/Managers/bmc/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002503 .privileges(redfish::privileges::postLogService)
Claire Weinanfdd26902022-03-01 14:18:25 -08002504 .methods(boost::beast::http::verb::post)(std::bind_front(
2505 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2506}
2507
2508inline void requestRoutesFaultLogDumpService(App& app)
2509{
2510 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/")
2511 .privileges(redfish::privileges::getLogService)
2512 .methods(boost::beast::http::verb::get)(std::bind_front(
2513 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2514}
2515
2516inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2517{
2518 BMCWEB_ROUTE(app, "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/")
2519 .privileges(redfish::privileges::getLogEntryCollection)
2520 .methods(boost::beast::http::verb::get)(
2521 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2522 std::ref(app), "FaultLog"));
2523}
2524
2525inline void requestRoutesFaultLogDumpEntry(App& app)
2526{
2527 BMCWEB_ROUTE(app,
2528 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2529 .privileges(redfish::privileges::getLogEntry)
2530 .methods(boost::beast::http::verb::get)(std::bind_front(
2531 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2532
2533 BMCWEB_ROUTE(app,
2534 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Entries/<str>/")
2535 .privileges(redfish::privileges::deleteLogEntry)
2536 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2537 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2538}
2539
2540inline void requestRoutesFaultLogDumpClear(App& app)
2541{
2542 BMCWEB_ROUTE(
2543 app,
2544 "/redfish/v1/Managers/bmc/LogServices/FaultLog/Actions/LogService.ClearLog/")
2545 .privileges(redfish::privileges::postLogService)
2546 .methods(boost::beast::http::verb::post)(std::bind_front(
2547 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002548}
2549
2550inline void requestRoutesSystemDumpService(App& app)
2551{
2552 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/")
Ed Tanoused398212021-06-09 17:05:54 -07002553 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07002554 .methods(boost::beast::http::verb::get)(
2555 [&app](const crow::Request& req,
2556 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002557 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002558 {
2559 return;
2560 }
2561 asyncResp->res.jsonValue["@odata.id"] =
2562 "/redfish/v1/Systems/system/LogServices/Dump";
2563 asyncResp->res.jsonValue["@odata.type"] =
2564 "#LogService.v1_2_0.LogService";
2565 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2566 asyncResp->res.jsonValue["Description"] = "System Dump LogService";
2567 asyncResp->res.jsonValue["Id"] = "Dump";
2568 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Tejas Patil7c8c4052021-06-04 17:43:14 +05302569
Ed Tanous002d39b2022-05-31 08:59:27 -07002570 std::pair<std::string, std::string> redfishDateTimeOffset =
2571 crow::utility::getDateTimeOffsetNow();
2572 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2573 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2574 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302575
Ed Tanous002d39b2022-05-31 08:59:27 -07002576 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2577 "/redfish/v1/Systems/system/LogServices/Dump/Entries";
2578 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2579 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog";
Ed Tanous14766872022-03-15 10:44:42 -07002580
Ed Tanous002d39b2022-05-31 08:59:27 -07002581 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2582 ["target"] =
2583 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData";
Ed Tanous45ca1b82022-03-25 13:07:27 -07002584 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002585}
2586
2587inline void requestRoutesSystemDumpEntryCollection(App& app)
2588{
2589
2590 /**
2591 * Functions triggers appropriate requests on DBus
2592 */
Asmitha Karunanithib2a32892021-07-13 11:56:15 -05002593 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Dump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002594 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002595 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002596 [&app](const crow::Request& req,
2597 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002598 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002599 {
2600 return;
2601 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002602 getDumpEntryCollection(asyncResp, "System");
2603 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002604}
2605
2606inline void requestRoutesSystemDumpEntry(App& app)
2607{
2608 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002609 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002610 .privileges(redfish::privileges::getLogEntry)
2611
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002612 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002613 [&app](const crow::Request& req,
2614 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2615 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002616 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Claire Weinanc7a6d662022-06-13 16:36:39 -07002617 {
2618 return;
2619 }
2620 getDumpEntryById(asyncResp, param, "System");
Ed Tanous002d39b2022-05-31 08:59:27 -07002621 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002622
2623 BMCWEB_ROUTE(app,
John Edward Broadbent864d6a12021-06-09 10:12:48 -07002624 "/redfish/v1/Systems/system/LogServices/Dump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002625 .privileges(redfish::privileges::deleteLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002626 .methods(boost::beast::http::verb::delete_)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002627 [&app](const crow::Request& req,
2628 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2629 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002630 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002631 {
2632 return;
2633 }
2634 deleteDumpEntry(asyncResp, param, "system");
2635 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002636}
2637
2638inline void requestRoutesSystemDumpCreate(App& app)
2639{
George Liu0fda0f12021-11-16 10:06:17 +08002640 BMCWEB_ROUTE(
2641 app,
2642 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07002643 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002644 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002645 [&app](const crow::Request& req,
2646 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002647 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002648 {
2649 return;
2650 }
2651 createDump(asyncResp, req, "System");
2652 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002653}
2654
2655inline void requestRoutesSystemDumpClear(App& app)
2656{
George Liu0fda0f12021-11-16 10:06:17 +08002657 BMCWEB_ROUTE(
2658 app,
2659 "/redfish/v1/Systems/system/LogServices/Dump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002660 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002661 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002662 [&app](const crow::Request& req,
2663 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002664
Ed Tanous45ca1b82022-03-25 13:07:27 -07002665 {
Carson Labrado3ba00072022-06-06 19:40:56 +00002666 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002667 {
2668 return;
2669 }
2670 clearDump(asyncResp, "System");
2671 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002672}
2673
2674inline void requestRoutesCrashdumpService(App& app)
2675{
2676 // Note: Deviated from redfish privilege registry for GET & HEAD
2677 // method for security reasons.
2678 /**
2679 * Functions triggers appropriate requests on DBus
2680 */
2681 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanoused398212021-06-09 17:05:54 -07002682 // This is incorrect, should be:
2683 //.privileges(redfish::privileges::getLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002684 .privileges({{"ConfigureManager"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002685 .methods(boost::beast::http::verb::get)(
2686 [&app](const crow::Request& req,
2687 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002688 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002689 {
2690 return;
2691 }
2692 // Copy over the static data to include the entries added by
2693 // SubRoute
2694 asyncResp->res.jsonValue["@odata.id"] =
2695 "/redfish/v1/Systems/system/LogServices/Crashdump";
2696 asyncResp->res.jsonValue["@odata.type"] =
2697 "#LogService.v1_2_0.LogService";
2698 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2699 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2700 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
2701 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
2702 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302703
Ed Tanous002d39b2022-05-31 08:59:27 -07002704 std::pair<std::string, std::string> redfishDateTimeOffset =
2705 crow::utility::getDateTimeOffsetNow();
2706 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2707 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2708 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05302709
Ed Tanous002d39b2022-05-31 08:59:27 -07002710 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
2711 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2712 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]["target"] =
2713 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog";
2714 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2715 ["target"] =
2716 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData";
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002717 });
2718}
2719
2720void inline requestRoutesCrashdumpClear(App& app)
2721{
George Liu0fda0f12021-11-16 10:06:17 +08002722 BMCWEB_ROUTE(
2723 app,
2724 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07002725 // This is incorrect, should be:
2726 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07002727 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002728 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002729 [&app](const crow::Request& req,
2730 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002731 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002732 {
2733 return;
2734 }
2735 crow::connections::systemBus->async_method_call(
2736 [asyncResp](const boost::system::error_code ec,
2737 const std::string&) {
2738 if (ec)
2739 {
2740 messages::internalError(asyncResp->res);
2741 return;
2742 }
2743 messages::success(asyncResp->res);
2744 },
2745 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2746 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002747}
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002748
zhanghch058d1b46d2021-04-01 11:18:24 +08002749static void
2750 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2751 const std::string& logID, nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002752{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002753 auto getStoredLogCallback =
Ed Tanousb9d36b42022-02-26 21:42:46 -08002754 [asyncResp, logID,
2755 &logEntryJson](const boost::system::error_code ec,
2756 const dbus::utility::DBusPropertiesMap& params) {
Ed Tanous002d39b2022-05-31 08:59:27 -07002757 if (ec)
2758 {
2759 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2760 if (ec.value() ==
2761 boost::system::linux_error::bad_request_descriptor)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002762 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002763 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002764 }
2765 else
2766 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002767 messages::internalError(asyncResp->res);
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002768 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002769 return;
2770 }
2771
2772 std::string timestamp{};
2773 std::string filename{};
2774 std::string logfile{};
2775 parseCrashdumpParameters(params, filename, timestamp, logfile);
2776
2777 if (filename.empty() || timestamp.empty())
2778 {
2779 messages::resourceMissingAtURI(asyncResp->res,
2780 crow::utility::urlFromPieces(logID));
2781 return;
2782 }
2783
2784 std::string crashdumpURI =
2785 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2786 logID + "/" + filename;
Jason M. Bills84afc482022-06-24 12:38:23 -07002787 nlohmann::json::object_t logEntry;
2788 logEntry["@odata.type"] = "#LogEntry.v1_7_0.LogEntry";
2789 logEntry["@odata.id"] =
2790 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" + logID;
2791 logEntry["Name"] = "CPU Crashdump";
2792 logEntry["Id"] = logID;
2793 logEntry["EntryType"] = "Oem";
2794 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2795 logEntry["DiagnosticDataType"] = "OEM";
2796 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2797 logEntry["Created"] = std::move(timestamp);
Ed Tanous002d39b2022-05-31 08:59:27 -07002798
2799 // If logEntryJson references an array of LogEntry resources
2800 // ('Members' list), then push this as a new entry, otherwise set it
2801 // directly
2802 if (logEntryJson.is_array())
2803 {
2804 logEntryJson.push_back(logEntry);
2805 asyncResp->res.jsonValue["Members@odata.count"] =
2806 logEntryJson.size();
2807 }
2808 else
2809 {
Jason M. Billsd405bb52022-06-24 10:52:05 -07002810 logEntryJson.update(logEntry);
Ed Tanous002d39b2022-05-31 08:59:27 -07002811 }
2812 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002813 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002814 std::move(getStoredLogCallback), crashdumpObject,
2815 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002816 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002817}
2818
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002819inline void requestRoutesCrashdumpEntryCollection(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002820{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002821 // Note: Deviated from redfish privilege registry for GET & HEAD
2822 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002823 /**
2824 * Functions triggers appropriate requests on DBus
2825 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002826 BMCWEB_ROUTE(app,
2827 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07002828 // This is incorrect, should be.
2829 //.privileges(redfish::privileges::postLogEntryCollection)
Ed Tanous432a8902021-06-14 15:28:56 -07002830 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07002831 .methods(boost::beast::http::verb::get)(
2832 [&app](const crow::Request& req,
2833 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002834 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002835 {
2836 return;
2837 }
2838 crow::connections::systemBus->async_method_call(
2839 [asyncResp](const boost::system::error_code ec,
2840 const std::vector<std::string>& resp) {
2841 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07002842 {
Ed Tanous002d39b2022-05-31 08:59:27 -07002843 if (ec.value() !=
2844 boost::system::errc::no_such_file_or_directory)
2845 {
2846 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2847 << ec.message();
2848 messages::internalError(asyncResp->res);
2849 return;
2850 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07002851 }
Ed Tanous002d39b2022-05-31 08:59:27 -07002852 asyncResp->res.jsonValue["@odata.type"] =
2853 "#LogEntryCollection.LogEntryCollection";
2854 asyncResp->res.jsonValue["@odata.id"] =
2855 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
2856 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
2857 asyncResp->res.jsonValue["Description"] =
2858 "Collection of Crashdump Entries";
2859 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2860 asyncResp->res.jsonValue["Members@odata.count"] = 0;
Jason M. Bills2b20ef62022-01-06 15:48:07 -08002861
Ed Tanous002d39b2022-05-31 08:59:27 -07002862 for (const std::string& path : resp)
2863 {
2864 const sdbusplus::message::object_path objPath(path);
2865 // Get the log ID
2866 std::string logID = objPath.filename();
2867 if (logID.empty())
2868 {
2869 continue;
2870 }
2871 // Add the log entry to the array
2872 logCrashdumpEntry(asyncResp, logID,
2873 asyncResp->res.jsonValue["Members"]);
2874 }
2875 },
2876 "xyz.openbmc_project.ObjectMapper",
2877 "/xyz/openbmc_project/object_mapper",
2878 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
2879 std::array<const char*, 1>{crashdumpInterface});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002880 });
2881}
Ed Tanous1da66f72018-07-27 16:13:37 -07002882
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002883inline void requestRoutesCrashdumpEntry(App& app)
Ed Tanous1da66f72018-07-27 16:13:37 -07002884{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002885 // Note: Deviated from redfish privilege registry for GET & HEAD
2886 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002887
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002888 BMCWEB_ROUTE(
2889 app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002890 // this is incorrect, should be
2891 // .privileges(redfish::privileges::getLogEntry)
Ed Tanous432a8902021-06-14 15:28:56 -07002892 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002893 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002894 [&app](const crow::Request& req,
2895 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2896 const std::string& param) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002897 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002898 {
2899 return;
2900 }
2901 const std::string& logID = param;
2902 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2903 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002904}
Ed Tanous1da66f72018-07-27 16:13:37 -07002905
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002906inline void requestRoutesCrashdumpFile(App& app)
2907{
2908 // Note: Deviated from redfish privilege registry for GET & HEAD
2909 // method for security reasons.
2910 BMCWEB_ROUTE(
2911 app,
2912 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07002913 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002914 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07002915 [&app](const crow::Request& req,
2916 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2917 const std::string& logID, const std::string& fileName) {
Carson Labrado3ba00072022-06-06 19:40:56 +00002918 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07002919 {
2920 return;
2921 }
2922 auto getStoredLogCallback =
2923 [asyncResp, logID, fileName, url(boost::urls::url(req.urlView))](
2924 const boost::system::error_code ec,
2925 const std::vector<
2926 std::pair<std::string, dbus::utility::DbusVariantType>>&
2927 resp) {
2928 if (ec)
2929 {
2930 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2931 messages::internalError(asyncResp->res);
2932 return;
2933 }
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002934
Ed Tanous002d39b2022-05-31 08:59:27 -07002935 std::string dbusFilename{};
2936 std::string dbusTimestamp{};
2937 std::string dbusFilepath{};
Jason M. Bills8e6c0992021-03-11 16:26:53 -08002938
Ed Tanous002d39b2022-05-31 08:59:27 -07002939 parseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2940 dbusFilepath);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002941
Ed Tanous002d39b2022-05-31 08:59:27 -07002942 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2943 dbusFilepath.empty())
2944 {
2945 messages::resourceMissingAtURI(asyncResp->res, url);
2946 return;
2947 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002948
Ed Tanous002d39b2022-05-31 08:59:27 -07002949 // Verify the file name parameter is correct
2950 if (fileName != dbusFilename)
2951 {
2952 messages::resourceMissingAtURI(asyncResp->res, url);
2953 return;
2954 }
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002955
Ed Tanous002d39b2022-05-31 08:59:27 -07002956 if (!std::filesystem::exists(dbusFilepath))
2957 {
2958 messages::resourceMissingAtURI(asyncResp->res, url);
2959 return;
2960 }
2961 std::ifstream ifs(dbusFilepath, std::ios::in | std::ios::binary);
2962 asyncResp->res.body() =
2963 std::string(std::istreambuf_iterator<char>{ifs}, {});
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002964
Ed Tanous002d39b2022-05-31 08:59:27 -07002965 // Configure this to be a file download when accessed
2966 // from a browser
2967 asyncResp->res.addHeader("Content-Disposition", "attachment");
2968 };
2969 crow::connections::systemBus->async_method_call(
2970 std::move(getStoredLogCallback), crashdumpObject,
2971 crashdumpPath + std::string("/") + logID,
2972 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
2973 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002974}
2975
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002976enum class OEMDiagnosticType
2977{
2978 onDemand,
2979 telemetry,
2980 invalid,
2981};
2982
Ed Tanousf7725d72022-03-07 12:46:00 -08002983inline OEMDiagnosticType
2984 getOEMDiagnosticType(const std::string_view& oemDiagStr)
Jason M. Billsc5a4c822022-01-06 15:51:23 -08002985{
2986 if (oemDiagStr == "OnDemand")
2987 {
2988 return OEMDiagnosticType::onDemand;
2989 }
2990 if (oemDiagStr == "Telemetry")
2991 {
2992 return OEMDiagnosticType::telemetry;
2993 }
2994
2995 return OEMDiagnosticType::invalid;
2996}
2997
John Edward Broadbent7e860f12021-04-08 15:57:16 -07002998inline void requestRoutesCrashdumpCollect(App& app)
2999{
3000 // Note: Deviated from redfish privilege registry for GET & HEAD
3001 // method for security reasons.
George Liu0fda0f12021-11-16 10:06:17 +08003002 BMCWEB_ROUTE(
3003 app,
3004 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
Ed Tanoused398212021-06-09 17:05:54 -07003005 // The below is incorrect; Should be ConfigureManager
3006 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003007 .privileges({{"ConfigureComponents"}})
Ed Tanous002d39b2022-05-31 08:59:27 -07003008 .methods(boost::beast::http::verb::post)(
3009 [&app](const crow::Request& req,
3010 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003011 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003012 {
3013 return;
3014 }
3015 std::string diagnosticDataType;
3016 std::string oemDiagnosticDataType;
3017 if (!redfish::json_util::readJsonAction(
3018 req, asyncResp->res, "DiagnosticDataType", diagnosticDataType,
3019 "OEMDiagnosticDataType", oemDiagnosticDataType))
3020 {
3021 return;
3022 }
3023
3024 if (diagnosticDataType != "OEM")
3025 {
3026 BMCWEB_LOG_ERROR
3027 << "Only OEM DiagnosticDataType supported for Crashdump";
3028 messages::actionParameterValueFormatError(
3029 asyncResp->res, diagnosticDataType, "DiagnosticDataType",
3030 "CollectDiagnosticData");
3031 return;
3032 }
3033
3034 OEMDiagnosticType oemDiagType =
3035 getOEMDiagnosticType(oemDiagnosticDataType);
3036
3037 std::string iface;
3038 std::string method;
3039 std::string taskMatchStr;
3040 if (oemDiagType == OEMDiagnosticType::onDemand)
3041 {
3042 iface = crashdumpOnDemandInterface;
3043 method = "GenerateOnDemandLog";
3044 taskMatchStr = "type='signal',"
3045 "interface='org.freedesktop.DBus.Properties',"
3046 "member='PropertiesChanged',"
3047 "arg0namespace='com.intel.crashdump'";
3048 }
3049 else if (oemDiagType == OEMDiagnosticType::telemetry)
3050 {
3051 iface = crashdumpTelemetryInterface;
3052 method = "GenerateTelemetryLog";
3053 taskMatchStr = "type='signal',"
3054 "interface='org.freedesktop.DBus.Properties',"
3055 "member='PropertiesChanged',"
3056 "arg0namespace='com.intel.crashdump'";
3057 }
3058 else
3059 {
3060 BMCWEB_LOG_ERROR << "Unsupported OEMDiagnosticDataType: "
3061 << oemDiagnosticDataType;
3062 messages::actionParameterValueFormatError(
3063 asyncResp->res, oemDiagnosticDataType, "OEMDiagnosticDataType",
3064 "CollectDiagnosticData");
3065 return;
3066 }
3067
3068 auto collectCrashdumpCallback =
3069 [asyncResp, payload(task::Payload(req)),
3070 taskMatchStr](const boost::system::error_code ec,
3071 const std::string&) mutable {
3072 if (ec)
Ed Tanous45ca1b82022-03-25 13:07:27 -07003073 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003074 if (ec.value() == boost::system::errc::operation_not_supported)
3075 {
3076 messages::resourceInStandby(asyncResp->res);
3077 }
3078 else if (ec.value() ==
3079 boost::system::errc::device_or_resource_busy)
3080 {
3081 messages::serviceTemporarilyUnavailable(asyncResp->res,
3082 "60");
3083 }
3084 else
3085 {
3086 messages::internalError(asyncResp->res);
3087 }
Ed Tanous45ca1b82022-03-25 13:07:27 -07003088 return;
3089 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003090 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
3091 [](boost::system::error_code err, sdbusplus::message::message&,
3092 const std::shared_ptr<task::TaskData>& taskData) {
3093 if (!err)
3094 {
3095 taskData->messages.emplace_back(messages::taskCompletedOK(
3096 std::to_string(taskData->index)));
3097 taskData->state = "Completed";
3098 }
3099 return task::completed;
3100 },
3101 taskMatchStr);
Ed Tanous1da66f72018-07-27 16:13:37 -07003102
Ed Tanous002d39b2022-05-31 08:59:27 -07003103 task->startTimer(std::chrono::minutes(5));
3104 task->populateResp(asyncResp->res);
3105 task->payload.emplace(std::move(payload));
3106 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003107
Ed Tanous002d39b2022-05-31 08:59:27 -07003108 crow::connections::systemBus->async_method_call(
3109 std::move(collectCrashdumpCallback), crashdumpObject, crashdumpPath,
3110 iface, method);
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003111 });
3112}
Kenny L. Ku6eda7682020-06-19 09:48:36 -07003113
Andrew Geisslercb92c032018-08-17 07:56:14 -07003114/**
3115 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3116 */
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003117inline void requestRoutesDBusLogServiceActionsClear(App& app)
Andrew Geisslercb92c032018-08-17 07:56:14 -07003118{
Andrew Geisslercb92c032018-08-17 07:56:14 -07003119 /**
3120 * Function handles POST method request.
3121 * The Clear Log actions does not require any parameter.The action deletes
3122 * all entries found in the Entries collection for this Log Service.
3123 */
Andrew Geisslercb92c032018-08-17 07:56:14 -07003124
George Liu0fda0f12021-11-16 10:06:17 +08003125 BMCWEB_ROUTE(
3126 app,
3127 "/redfish/v1/Systems/system/LogServices/EventLog/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003128 .privileges(redfish::privileges::postLogService)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003129 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003130 [&app](const crow::Request& req,
3131 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003132 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003133 {
3134 return;
3135 }
3136 BMCWEB_LOG_DEBUG << "Do delete all entries.";
Andrew Geisslercb92c032018-08-17 07:56:14 -07003137
Ed Tanous002d39b2022-05-31 08:59:27 -07003138 // Process response from Logging service.
3139 auto respHandler = [asyncResp](const boost::system::error_code ec) {
3140 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
3141 if (ec)
3142 {
3143 // TODO Handle for specific error code
3144 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
3145 asyncResp->res.result(
3146 boost::beast::http::status::internal_server_error);
3147 return;
3148 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07003149
Ed Tanous002d39b2022-05-31 08:59:27 -07003150 asyncResp->res.result(boost::beast::http::status::no_content);
3151 };
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003152
Ed Tanous002d39b2022-05-31 08:59:27 -07003153 // Make call to Logging service to request Clear Log
3154 crow::connections::systemBus->async_method_call(
3155 respHandler, "xyz.openbmc_project.Logging",
3156 "/xyz/openbmc_project/logging",
3157 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3158 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003159}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003160
3161/****************************************************
3162 * Redfish PostCode interfaces
3163 * using DBUS interface: getPostCodesTS
3164 ******************************************************/
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003165inline void requestRoutesPostCodesLogService(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003166{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003167 BMCWEB_ROUTE(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
Ed Tanoused398212021-06-09 17:05:54 -07003168 .privileges(redfish::privileges::getLogService)
Ed Tanous002d39b2022-05-31 08:59:27 -07003169 .methods(boost::beast::http::verb::get)(
3170 [&app](const crow::Request& req,
3171 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003172 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003173 {
3174 return;
3175 }
Ed Tanous14766872022-03-15 10:44:42 -07003176
Ed Tanous002d39b2022-05-31 08:59:27 -07003177 asyncResp->res.jsonValue["@odata.id"] =
3178 "/redfish/v1/Systems/system/LogServices/PostCodes";
3179 asyncResp->res.jsonValue["@odata.type"] =
3180 "#LogService.v1_1_0.LogService";
3181 asyncResp->res.jsonValue["Name"] = "POST Code Log Service";
3182 asyncResp->res.jsonValue["Description"] = "POST Code Log Service";
3183 asyncResp->res.jsonValue["Id"] = "BIOS POST Code Log";
3184 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
3185 asyncResp->res.jsonValue["Entries"]["@odata.id"] =
3186 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
Tejas Patil7c8c4052021-06-04 17:43:14 +05303187
Ed Tanous002d39b2022-05-31 08:59:27 -07003188 std::pair<std::string, std::string> redfishDateTimeOffset =
3189 crow::utility::getDateTimeOffsetNow();
3190 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
3191 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
3192 redfishDateTimeOffset.second;
Tejas Patil7c8c4052021-06-04 17:43:14 +05303193
Ed Tanous002d39b2022-05-31 08:59:27 -07003194 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
3195 {"target",
3196 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog"}};
George Liu0fda0f12021-11-16 10:06:17 +08003197 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003198}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003199
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003200inline void requestRoutesPostCodesClear(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003201{
George Liu0fda0f12021-11-16 10:06:17 +08003202 BMCWEB_ROUTE(
3203 app,
3204 "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/LogService.ClearLog/")
Ed Tanoused398212021-06-09 17:05:54 -07003205 // The following privilege is incorrect; It should be ConfigureManager
3206 //.privileges(redfish::privileges::postLogService)
Ed Tanous432a8902021-06-14 15:28:56 -07003207 .privileges({{"ConfigureComponents"}})
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003208 .methods(boost::beast::http::verb::post)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003209 [&app](const crow::Request& req,
3210 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003211 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003212 {
3213 return;
3214 }
3215 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
ZhikuiRena3316fc2020-01-29 14:58:08 -08003216
Ed Tanous002d39b2022-05-31 08:59:27 -07003217 // Make call to post-code service to request clear all
3218 crow::connections::systemBus->async_method_call(
3219 [asyncResp](const boost::system::error_code ec) {
3220 if (ec)
3221 {
3222 // TODO Handle for specific error code
3223 BMCWEB_LOG_ERROR << "doClearPostCodes resp_handler got error "
3224 << ec;
3225 asyncResp->res.result(
3226 boost::beast::http::status::internal_server_error);
3227 messages::internalError(asyncResp->res);
3228 return;
3229 }
3230 },
3231 "xyz.openbmc_project.State.Boot.PostCode0",
3232 "/xyz/openbmc_project/State/Boot/PostCode0",
3233 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3234 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003235}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003236
3237static void fillPostCodeEntry(
zhanghch058d1b46d2021-04-01 11:18:24 +08003238 const std::shared_ptr<bmcweb::AsyncResp>& aResp,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303239 const boost::container::flat_map<
3240 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003241 const uint16_t bootIndex, const uint64_t codeIndex = 0,
3242 const uint64_t skip = 0, const uint64_t top = 0)
3243{
3244 // Get the Message from the MessageRegistry
Ed Tanousfffb8c12022-02-07 23:53:03 -08003245 const registries::Message* message =
3246 registries::getMessage("OpenBMC.0.2.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08003247
3248 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003249 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003250
3251 uint64_t firstCodeTimeUs = 0;
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303252 for (const std::pair<uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3253 code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003254 {
3255 currentCodeIndex++;
3256 std::string postcodeEntryID =
3257 "B" + std::to_string(bootIndex) + "-" +
3258 std::to_string(currentCodeIndex); // 1 based index in EntryID string
3259
3260 uint64_t usecSinceEpoch = code.first;
3261 uint64_t usTimeOffset = 0;
3262
3263 if (1 == currentCodeIndex)
3264 { // already incremented
3265 firstCodeTimeUs = code.first;
3266 }
3267 else
3268 {
3269 usTimeOffset = code.first - firstCodeTimeUs;
3270 }
3271
3272 // skip if no specific codeIndex is specified and currentCodeIndex does
3273 // not fall between top and skip
3274 if ((codeIndex == 0) &&
3275 (currentCodeIndex <= skip || currentCodeIndex > top))
3276 {
3277 continue;
3278 }
3279
Gunnar Mills4e0453b2020-07-08 14:00:30 -05003280 // skip if a specific codeIndex is specified and does not match the
ZhikuiRena3316fc2020-01-29 14:58:08 -08003281 // currentIndex
3282 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
3283 {
3284 // This is done for simplicity. 1st entry is needed to calculate
3285 // time offset. To improve efficiency, one can get to the entry
3286 // directly (possibly with flatmap's nth method)
3287 continue;
3288 }
3289
3290 // currentCodeIndex is within top and skip or equal to specified code
3291 // index
3292
3293 // Get the Created time from the timestamp
3294 std::string entryTimeStr;
Nan Zhou1d8782e2021-11-29 22:23:18 -08003295 entryTimeStr =
3296 crow::utility::getDateTimeUint(usecSinceEpoch / 1000 / 1000);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003297
3298 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
3299 std::ostringstream hexCode;
3300 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303301 << std::get<0>(code.second);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003302 std::ostringstream timeOffsetStr;
3303 // Set Fixed -Point Notation
3304 timeOffsetStr << std::fixed;
3305 // Set precision to 4 digits
3306 timeOffsetStr << std::setprecision(4);
3307 // Add double to stream
3308 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
3309 std::vector<std::string> messageArgs = {
3310 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
3311
3312 // Get MessageArgs template from message registry
3313 std::string msg;
3314 if (message != nullptr)
3315 {
3316 msg = message->message;
3317
3318 // fill in this post code value
3319 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003320 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003321 {
3322 std::string argStr = "%" + std::to_string(++i);
3323 size_t argPos = msg.find(argStr);
3324 if (argPos != std::string::npos)
3325 {
3326 msg.replace(argPos, argStr.length(), messageArg);
3327 }
3328 }
3329 }
3330
Tim Leed4342a92020-04-27 11:47:58 +08003331 // Get Severity template from message registry
3332 std::string severity;
3333 if (message != nullptr)
3334 {
Ed Tanous5f2b84e2022-02-08 00:41:53 -08003335 severity = message->messageSeverity;
Tim Leed4342a92020-04-27 11:47:58 +08003336 }
3337
ZhikuiRena3316fc2020-01-29 14:58:08 -08003338 // add to AsyncResp
3339 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003340 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills84afc482022-06-24 12:38:23 -07003341 bmcLogEntry["@odata.type"] = "#LogEntry.v1_8_0.LogEntry";
3342 bmcLogEntry["@odata.id"] =
3343 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3344 postcodeEntryID;
3345 bmcLogEntry["Name"] = "POST Code Log Entry";
3346 bmcLogEntry["Id"] = postcodeEntryID;
3347 bmcLogEntry["Message"] = std::move(msg);
3348 bmcLogEntry["MessageId"] = "OpenBMC.0.2.BIOSPOSTCode";
3349 bmcLogEntry["MessageArgs"] = std::move(messageArgs);
3350 bmcLogEntry["EntryType"] = "Event";
3351 bmcLogEntry["Severity"] = std::move(severity);
3352 bmcLogEntry["Created"] = entryTimeStr;
George Liu647b3cd2021-07-05 12:43:56 +08003353 if (!std::get<std::vector<uint8_t>>(code.second).empty())
3354 {
3355 bmcLogEntry["AdditionalDataURI"] =
3356 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/" +
3357 postcodeEntryID + "/attachment";
3358 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003359 }
3360}
3361
zhanghch058d1b46d2021-04-01 11:18:24 +08003362static void getPostCodeForEntry(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003363 const uint16_t bootIndex,
3364 const uint64_t codeIndex)
3365{
3366 crow::connections::systemBus->async_method_call(
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303367 [aResp, bootIndex,
3368 codeIndex](const boost::system::error_code ec,
3369 const boost::container::flat_map<
3370 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3371 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003372 if (ec)
3373 {
3374 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3375 messages::internalError(aResp->res);
3376 return;
3377 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003378
Ed Tanous002d39b2022-05-31 08:59:27 -07003379 // skip the empty postcode boots
3380 if (postcode.empty())
3381 {
3382 return;
3383 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003384
Ed Tanous002d39b2022-05-31 08:59:27 -07003385 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003386
Ed Tanous002d39b2022-05-31 08:59:27 -07003387 aResp->res.jsonValue["Members@odata.count"] =
3388 aResp->res.jsonValue["Members"].size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003389 },
Jonathan Doman15124762021-01-07 17:54:17 -08003390 "xyz.openbmc_project.State.Boot.PostCode0",
3391 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003392 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3393 bootIndex);
3394}
3395
zhanghch058d1b46d2021-04-01 11:18:24 +08003396static void getPostCodeForBoot(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
ZhikuiRena3316fc2020-01-29 14:58:08 -08003397 const uint16_t bootIndex,
3398 const uint16_t bootCount,
3399 const uint64_t entryCount, const uint64_t skip,
3400 const uint64_t top)
3401{
3402 crow::connections::systemBus->async_method_call(
3403 [aResp, bootIndex, bootCount, entryCount, skip,
3404 top](const boost::system::error_code ec,
Manojkiran Eda6c9a2792021-02-27 14:25:04 +05303405 const boost::container::flat_map<
3406 uint64_t, std::tuple<uint64_t, std::vector<uint8_t>>>&
3407 postcode) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003408 if (ec)
3409 {
3410 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
3411 messages::internalError(aResp->res);
3412 return;
3413 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003414
Ed Tanous002d39b2022-05-31 08:59:27 -07003415 uint64_t endCount = entryCount;
3416 if (!postcode.empty())
3417 {
3418 endCount = entryCount + postcode.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -08003419
Ed Tanous002d39b2022-05-31 08:59:27 -07003420 if ((skip < endCount) && ((top + skip) > entryCount))
ZhikuiRena3316fc2020-01-29 14:58:08 -08003421 {
Ed Tanous002d39b2022-05-31 08:59:27 -07003422 uint64_t thisBootSkip = std::max(skip, entryCount) - entryCount;
3423 uint64_t thisBootTop =
3424 std::min(top + skip, endCount) - entryCount;
3425
3426 fillPostCodeEntry(aResp, postcode, bootIndex, 0, thisBootSkip,
3427 thisBootTop);
ZhikuiRena3316fc2020-01-29 14:58:08 -08003428 }
Ed Tanous002d39b2022-05-31 08:59:27 -07003429 aResp->res.jsonValue["Members@odata.count"] = endCount;
3430 }
3431
3432 // continue to previous bootIndex
3433 if (bootIndex < bootCount)
3434 {
3435 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
3436 bootCount, endCount, skip, top);
3437 }
3438 else
3439 {
3440 aResp->res.jsonValue["Members@odata.nextLink"] =
3441 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries?$skip=" +
3442 std::to_string(skip + top);
3443 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003444 },
Jonathan Doman15124762021-01-07 17:54:17 -08003445 "xyz.openbmc_project.State.Boot.PostCode0",
3446 "/xyz/openbmc_project/State/Boot/PostCode0",
ZhikuiRena3316fc2020-01-29 14:58:08 -08003447 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
3448 bootIndex);
3449}
3450
zhanghch058d1b46d2021-04-01 11:18:24 +08003451static void
3452 getCurrentBootNumber(const std::shared_ptr<bmcweb::AsyncResp>& aResp,
3453 const uint64_t skip, const uint64_t top)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003454{
3455 uint64_t entryCount = 0;
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003456 sdbusplus::asio::getProperty<uint16_t>(
3457 *crow::connections::systemBus,
3458 "xyz.openbmc_project.State.Boot.PostCode0",
3459 "/xyz/openbmc_project/State/Boot/PostCode0",
3460 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount",
3461 [aResp, entryCount, skip, top](const boost::system::error_code ec,
3462 const uint16_t bootCount) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003463 if (ec)
3464 {
3465 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3466 messages::internalError(aResp->res);
3467 return;
3468 }
3469 getPostCodeForBoot(aResp, 1, bootCount, entryCount, skip, top);
Jonathan Doman1e1e5982021-06-11 09:36:17 -07003470 });
ZhikuiRena3316fc2020-01-29 14:58:08 -08003471}
3472
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003473inline void requestRoutesPostCodesEntryCollection(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003474{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003475 BMCWEB_ROUTE(app,
3476 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
Ed Tanoused398212021-06-09 17:05:54 -07003477 .privileges(redfish::privileges::getLogEntryCollection)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003478 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003479 [&app](const crow::Request& req,
3480 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) {
Ed Tanous002d39b2022-05-31 08:59:27 -07003481 query_param::QueryCapabilities capabilities = {
3482 .canDelegateTop = true,
3483 .canDelegateSkip = true,
3484 };
3485 query_param::Query delegatedQuery;
3486 if (!redfish::setUpRedfishRouteWithDelegation(
Carson Labrado3ba00072022-06-06 19:40:56 +00003487 app, req, asyncResp, delegatedQuery, capabilities))
Ed Tanous002d39b2022-05-31 08:59:27 -07003488 {
3489 return;
3490 }
3491 asyncResp->res.jsonValue["@odata.type"] =
3492 "#LogEntryCollection.LogEntryCollection";
3493 asyncResp->res.jsonValue["@odata.id"] =
3494 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3495 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3496 asyncResp->res.jsonValue["Description"] =
3497 "Collection of POST Code Log Entries";
3498 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3499 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003500
Ed Tanous002d39b2022-05-31 08:59:27 -07003501 getCurrentBootNumber(asyncResp, delegatedQuery.skip,
3502 delegatedQuery.top);
3503 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003504}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003505
George Liu647b3cd2021-07-05 12:43:56 +08003506/**
3507 * @brief Parse post code ID and get the current value and index value
3508 * eg: postCodeID=B1-2, currentValue=1, index=2
3509 *
3510 * @param[in] postCodeID Post Code ID
3511 * @param[out] currentValue Current value
3512 * @param[out] index Index value
3513 *
3514 * @return bool true if the parsing is successful, false the parsing fails
3515 */
3516inline static bool parsePostCode(const std::string& postCodeID,
3517 uint64_t& currentValue, uint16_t& index)
3518{
3519 std::vector<std::string> split;
3520 boost::algorithm::split(split, postCodeID, boost::is_any_of("-"));
3521 if (split.size() != 2 || split[0].length() < 2 || split[0].front() != 'B')
3522 {
3523 return false;
3524 }
3525
Ed Tanousca45aa32022-01-07 09:28:45 -08003526 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003527 const char* start = split[0].data() + 1;
Ed Tanousca45aa32022-01-07 09:28:45 -08003528 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003529 const char* end = split[0].data() + split[0].size();
3530 auto [ptrIndex, ecIndex] = std::from_chars(start, end, index);
3531
3532 if (ptrIndex != end || ecIndex != std::errc())
3533 {
3534 return false;
3535 }
3536
3537 start = split[1].data();
Ed Tanousca45aa32022-01-07 09:28:45 -08003538
3539 // NOLINTNEXTLINE(cppcoreguidelines-pro-bounds-pointer-arithmetic)
George Liu647b3cd2021-07-05 12:43:56 +08003540 end = split[1].data() + split[1].size();
3541 auto [ptrValue, ecValue] = std::from_chars(start, end, currentValue);
George Liu647b3cd2021-07-05 12:43:56 +08003542
Tony Lee517d9a52022-06-28 15:41:23 +08003543 return ptrValue == end && ecValue == std::errc();
George Liu647b3cd2021-07-05 12:43:56 +08003544}
3545
3546inline void requestRoutesPostCodesEntryAdditionalData(App& app)
3547{
George Liu0fda0f12021-11-16 10:06:17 +08003548 BMCWEB_ROUTE(
3549 app,
3550 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/attachment/")
George Liu647b3cd2021-07-05 12:43:56 +08003551 .privileges(redfish::privileges::getLogEntry)
3552 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003553 [&app](const crow::Request& req,
3554 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3555 const std::string& postCodeID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003556 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003557 {
3558 return;
3559 }
3560 if (!http_helpers::isOctetAccepted(req.getHeaderValue("Accept")))
3561 {
3562 asyncResp->res.result(boost::beast::http::status::bad_request);
3563 return;
3564 }
George Liu647b3cd2021-07-05 12:43:56 +08003565
Ed Tanous002d39b2022-05-31 08:59:27 -07003566 uint64_t currentValue = 0;
3567 uint16_t index = 0;
3568 if (!parsePostCode(postCodeID, currentValue, index))
3569 {
3570 messages::resourceNotFound(asyncResp->res, "LogEntry", postCodeID);
3571 return;
3572 }
George Liu647b3cd2021-07-05 12:43:56 +08003573
Ed Tanous002d39b2022-05-31 08:59:27 -07003574 crow::connections::systemBus->async_method_call(
3575 [asyncResp, postCodeID, currentValue](
3576 const boost::system::error_code ec,
3577 const std::vector<std::tuple<uint64_t, std::vector<uint8_t>>>&
3578 postcodes) {
3579 if (ec.value() == EBADR)
3580 {
3581 messages::resourceNotFound(asyncResp->res, "LogEntry",
3582 postCodeID);
3583 return;
3584 }
3585 if (ec)
3586 {
3587 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
3588 messages::internalError(asyncResp->res);
3589 return;
3590 }
George Liu647b3cd2021-07-05 12:43:56 +08003591
Ed Tanous002d39b2022-05-31 08:59:27 -07003592 size_t value = static_cast<size_t>(currentValue) - 1;
3593 if (value == std::string::npos || postcodes.size() < currentValue)
3594 {
3595 BMCWEB_LOG_ERROR << "Wrong currentValue value";
3596 messages::resourceNotFound(asyncResp->res, "LogEntry",
3597 postCodeID);
3598 return;
3599 }
George Liu647b3cd2021-07-05 12:43:56 +08003600
Ed Tanous002d39b2022-05-31 08:59:27 -07003601 const auto& [tID, c] = postcodes[value];
3602 if (c.empty())
3603 {
3604 BMCWEB_LOG_INFO << "No found post code data";
3605 messages::resourceNotFound(asyncResp->res, "LogEntry",
3606 postCodeID);
3607 return;
3608 }
3609 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast)
3610 const char* d = reinterpret_cast<const char*>(c.data());
3611 std::string_view strData(d, c.size());
George Liu647b3cd2021-07-05 12:43:56 +08003612
Ed Tanous002d39b2022-05-31 08:59:27 -07003613 asyncResp->res.addHeader("Content-Type",
3614 "application/octet-stream");
3615 asyncResp->res.addHeader("Content-Transfer-Encoding", "Base64");
3616 asyncResp->res.body() = crow::utility::base64encode(strData);
3617 },
3618 "xyz.openbmc_project.State.Boot.PostCode0",
3619 "/xyz/openbmc_project/State/Boot/PostCode0",
3620 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodes", index);
3621 });
George Liu647b3cd2021-07-05 12:43:56 +08003622}
3623
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003624inline void requestRoutesPostCodesEntry(App& app)
ZhikuiRena3316fc2020-01-29 14:58:08 -08003625{
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003626 BMCWEB_ROUTE(
3627 app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/")
Ed Tanoused398212021-06-09 17:05:54 -07003628 .privileges(redfish::privileges::getLogEntry)
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003629 .methods(boost::beast::http::verb::get)(
Ed Tanous45ca1b82022-03-25 13:07:27 -07003630 [&app](const crow::Request& req,
3631 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3632 const std::string& targetID) {
Carson Labrado3ba00072022-06-06 19:40:56 +00003633 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
Ed Tanous002d39b2022-05-31 08:59:27 -07003634 {
3635 return;
3636 }
3637 uint16_t bootIndex = 0;
3638 uint64_t codeIndex = 0;
3639 if (!parsePostCode(targetID, codeIndex, bootIndex))
3640 {
3641 // Requested ID was not found
3642 messages::resourceMissingAtURI(asyncResp->res, req.urlView);
3643 return;
3644 }
3645 if (bootIndex == 0 || codeIndex == 0)
3646 {
3647 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3648 << targetID;
3649 }
ZhikuiRena3316fc2020-01-29 14:58:08 -08003650
Ed Tanous002d39b2022-05-31 08:59:27 -07003651 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3652 asyncResp->res.jsonValue["@odata.id"] =
3653 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
3654 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3655 asyncResp->res.jsonValue["Description"] =
3656 "Collection of POST Code Log Entries";
3657 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3658 asyncResp->res.jsonValue["Members@odata.count"] = 0;
ZhikuiRena3316fc2020-01-29 14:58:08 -08003659
Ed Tanous002d39b2022-05-31 08:59:27 -07003660 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3661 });
John Edward Broadbent7e860f12021-04-08 15:57:16 -07003662}
ZhikuiRena3316fc2020-01-29 14:58:08 -08003663
Ed Tanous1da66f72018-07-27 16:13:37 -07003664} // namespace redfish