blob: 52a51a60b93934093f4ec0e1db96c64956e101f0 [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
18#include "node.hpp"
Jason M. Bills4851d452019-03-28 11:27:48 -070019#include "registries.hpp"
20#include "registries/base_message_registry.hpp"
21#include "registries/openbmc_message_registry.hpp"
James Feist46229572020-02-19 15:11:58 -080022#include "task.hpp"
Ed Tanous1da66f72018-07-27 16:13:37 -070023
Jason M. Billse1f26342018-07-18 12:12:00 -070024#include <systemd/sd-journal.h>
25
Jason M. Bills4851d452019-03-28 11:27:48 -070026#include <boost/algorithm/string/split.hpp>
27#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070028#include <boost/container/flat_map.hpp>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080029#include <boost/system/linux_error.hpp>
raviteja-b06578432020-02-03 12:50:42 -060030#include <dump_offload.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070031#include <error_messages.hpp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050032
James Feist4418c7f2019-04-15 11:09:15 -070033#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070034#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080035#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070036
37namespace redfish
38{
39
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040constexpr char const* crashdumpObject = "com.intel.crashdump";
41constexpr char const* crashdumpPath = "/com/intel/crashdump";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050042constexpr char const* crashdumpInterface = "com.intel.crashdump";
43constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070044 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050045constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070046 "com.intel.crashdump.OnDemand";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050047constexpr char const* crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070048 "com.intel.crashdump.SendRawPeci";
Kenny L. Ku6eda7682020-06-19 09:48:36 -070049constexpr char const* crashdumpTelemetryInterface =
50 "com.intel.crashdump.Telemetry";
Ed Tanous1da66f72018-07-27 16:13:37 -070051
Jason M. Bills4851d452019-03-28 11:27:48 -070052namespace message_registries
53{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050054static const Message* getMessageFromRegistry(
55 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070056 const boost::beast::span<const MessageEntry> registry)
57{
58 boost::beast::span<const MessageEntry>::const_iterator messageIt =
59 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050060 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070061 return !std::strcmp(messageEntry.first,
62 messageKey.c_str());
63 });
64 if (messageIt != registry.cend())
65 {
66 return &messageIt->second;
67 }
68
69 return nullptr;
70}
71
Gunnar Mills1214b7e2020-06-04 10:11:30 -050072static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070073{
74 // Redfish MessageIds are in the form
75 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
76 // the right Message
77 std::vector<std::string> fields;
78 fields.reserve(4);
79 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050080 std::string& registryName = fields[0];
81 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070082
83 // Find the right registry and check it for the MessageKey
84 if (std::string(base::header.registryPrefix) == registryName)
85 {
86 return getMessageFromRegistry(
87 messageKey, boost::beast::span<const MessageEntry>(base::registry));
88 }
89 if (std::string(openbmc::header.registryPrefix) == registryName)
90 {
91 return getMessageFromRegistry(
92 messageKey,
93 boost::beast::span<const MessageEntry>(openbmc::registry));
94 }
95 return nullptr;
96}
97} // namespace message_registries
98
James Feistf6150402019-01-08 10:36:20 -080099namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -0700100
Andrew Geisslercb92c032018-08-17 07:56:14 -0700101using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500102 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
103 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700104
105using GetManagedObjectsType = boost::container::flat_map<
106 sdbusplus::message::object_path,
107 boost::container::flat_map<std::string, GetManagedPropertyType>>;
108
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500109inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700110{
111 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
112 {
113 return "Critical";
114 }
115 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
116 {
117 return "Critical";
118 }
119 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
120 {
121 return "OK";
122 }
123 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
124 {
125 return "Critical";
126 }
127 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
128 {
129 return "Critical";
130 }
131 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
132 {
133 return "OK";
134 }
135 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
136 {
137 return "OK";
138 }
139 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
140 {
141 return "Warning";
142 }
143 return "";
144}
145
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500146inline void deleteSystemDumpEntry(crow::Response& res,
147 const std::string& entryID)
raviteja-bc9bb6862020-02-03 11:53:32 -0600148{
149 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
150
151 auto respHandler = [asyncResp](const boost::system::error_code ec) {
152 BMCWEB_LOG_DEBUG << "System Dump Entry doDelete callback: Done";
153 if (ec)
154 {
155 BMCWEB_LOG_ERROR
156 << "System Dump (DBus) doDelete respHandler got error " << ec;
157 asyncResp->res.result(
158 boost::beast::http::status::internal_server_error);
159 return;
160 }
raviteja-bc9bb6862020-02-03 11:53:32 -0600161 };
162 crow::connections::systemBus->async_method_call(
163 respHandler, "xyz.openbmc_project.Dump.Manager",
164 "/xyz/openbmc_project/dump/entry/" + entryID,
165 "xyz.openbmc_project.Object.Delete", "Delete");
166}
167
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500168static int getJournalMetadata(sd_journal* journal,
169 const std::string_view& field,
170 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700171{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500172 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700173 size_t length = 0;
174 int ret = 0;
175 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700176 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500177 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700178 if (ret < 0)
179 {
180 return ret;
181 }
Ed Tanous39e77502019-03-04 17:35:53 -0800182 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700183 // Only use the content after the "=" character.
184 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
185 return ret;
186}
187
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500188static int getJournalMetadata(sd_journal* journal,
189 const std::string_view& field, const int& base,
190 long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700191{
192 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800193 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700194 // Get the metadata from the requested field of the journal entry
195 ret = getJournalMetadata(journal, field, metadata);
196 if (ret < 0)
197 {
198 return ret;
199 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000200 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700201 return ret;
202}
203
ZhikuiRena3316fc2020-01-29 14:58:08 -0800204static bool getTimestampStr(const uint64_t usecSinceEpoch,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500205 std::string& entryTimestamp)
Jason M. Bills16428a12018-11-02 12:42:29 -0700206{
ZhikuiRena3316fc2020-01-29 14:58:08 -0800207 time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500208 struct tm* loctime = localtime(&t);
Jason M. Bills16428a12018-11-02 12:42:29 -0700209 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700210 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700211 {
212 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
213 }
214 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800215 std::string_view t1(entryTime);
216 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700217 if (t1.size() > 2 && t2.size() > 2)
218 {
219 t1.remove_suffix(2);
220 t2.remove_prefix(t2.size() - 2);
221 }
Ed Tanous39e77502019-03-04 17:35:53 -0800222 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700223 return true;
224}
225
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500226static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800227{
228 int ret = 0;
229 uint64_t timestamp = 0;
230 ret = sd_journal_get_realtime_usec(journal, &timestamp);
231 if (ret < 0)
232 {
233 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
234 << strerror(-ret);
235 return false;
236 }
237 return getTimestampStr(timestamp, entryTimestamp);
238}
239
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500240static bool getSkipParam(crow::Response& res, const crow::Request& req,
241 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700242{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500243 char* skipParam = req.urlParams.get("$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700244 if (skipParam != nullptr)
245 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500246 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700247 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700248 if (*skipParam == '\0' || *ptr != '\0')
249 {
250
251 messages::queryParameterValueTypeError(res, std::string(skipParam),
252 "$skip");
253 return false;
254 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700255 }
256 return true;
257}
258
Ed Tanous271584a2019-07-09 16:24:22 -0700259static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500260static bool getTopParam(crow::Response& res, const crow::Request& req,
261 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700262{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500263 char* topParam = req.urlParams.get("$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700264 if (topParam != nullptr)
265 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500266 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700267 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700268 if (*topParam == '\0' || *ptr != '\0')
269 {
270 messages::queryParameterValueTypeError(res, std::string(topParam),
271 "$top");
272 return false;
273 }
Ed Tanous271584a2019-07-09 16:24:22 -0700274 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700275 {
276
277 messages::queryParameterOutOfRange(
278 res, std::to_string(top), "$top",
279 "1-" + std::to_string(maxEntriesPerPage));
280 return false;
281 }
282 }
283 return true;
284}
285
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500286static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700287 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700288{
289 int ret = 0;
290 static uint64_t prevTs = 0;
291 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700292 if (firstEntry)
293 {
294 prevTs = 0;
295 }
296
Jason M. Bills16428a12018-11-02 12:42:29 -0700297 // Get the entry timestamp
298 uint64_t curTs = 0;
299 ret = sd_journal_get_realtime_usec(journal, &curTs);
300 if (ret < 0)
301 {
302 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
303 << strerror(-ret);
304 return false;
305 }
306 // If the timestamp isn't unique, increment the index
307 if (curTs == prevTs)
308 {
309 index++;
310 }
311 else
312 {
313 // Otherwise, reset it
314 index = 0;
315 }
316 // Save the timestamp
317 prevTs = curTs;
318
319 entryID = std::to_string(curTs);
320 if (index > 0)
321 {
322 entryID += "_" + std::to_string(index);
323 }
324 return true;
325}
326
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500327static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700328 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700329{
Ed Tanous271584a2019-07-09 16:24:22 -0700330 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700331 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700332 if (firstEntry)
333 {
334 prevTs = 0;
335 }
336
Jason M. Bills95820182019-04-22 16:25:34 -0700337 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700338 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700339 std::tm timeStruct = {};
340 std::istringstream entryStream(logEntry);
341 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
342 {
343 curTs = std::mktime(&timeStruct);
344 }
345 // If the timestamp isn't unique, increment the index
346 if (curTs == prevTs)
347 {
348 index++;
349 }
350 else
351 {
352 // Otherwise, reset it
353 index = 0;
354 }
355 // Save the timestamp
356 prevTs = curTs;
357
358 entryID = std::to_string(curTs);
359 if (index > 0)
360 {
361 entryID += "_" + std::to_string(index);
362 }
363 return true;
364}
365
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500366static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
367 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700368{
369 if (entryID.empty())
370 {
371 return false;
372 }
373 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800374 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700375
376 auto underscorePos = tsStr.find("_");
377 if (underscorePos != tsStr.npos)
378 {
379 // Timestamp has an index
380 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800381 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700382 indexStr.remove_prefix(underscorePos + 1);
383 std::size_t pos;
384 try
385 {
Ed Tanous39e77502019-03-04 17:35:53 -0800386 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700387 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500388 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700389 {
390 messages::resourceMissingAtURI(res, entryID);
391 return false;
392 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500393 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700394 {
395 messages::resourceMissingAtURI(res, entryID);
396 return false;
397 }
398 if (pos != indexStr.size())
399 {
400 messages::resourceMissingAtURI(res, entryID);
401 return false;
402 }
403 }
404 // Timestamp has no index
405 std::size_t pos;
406 try
407 {
Ed Tanous39e77502019-03-04 17:35:53 -0800408 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700409 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500410 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700411 {
412 messages::resourceMissingAtURI(res, entryID);
413 return false;
414 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500415 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700416 {
417 messages::resourceMissingAtURI(res, entryID);
418 return false;
419 }
420 if (pos != tsStr.size())
421 {
422 messages::resourceMissingAtURI(res, entryID);
423 return false;
424 }
425 return true;
426}
427
Jason M. Bills95820182019-04-22 16:25:34 -0700428static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500429 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700430{
431 static const std::filesystem::path redfishLogDir = "/var/log";
432 static const std::string redfishLogFilename = "redfish";
433
434 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500435 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700436 std::filesystem::directory_iterator(redfishLogDir))
437 {
438 // If we find a redfish log file, save the path
439 std::string filename = dirEnt.path().filename();
440 if (boost::starts_with(filename, redfishLogFilename))
441 {
442 redfishLogFiles.emplace_back(redfishLogDir / filename);
443 }
444 }
445 // As the log files rotate, they are appended with a ".#" that is higher for
446 // the older logs. Since we don't expect more than 10 log files, we
447 // can just sort the list to get them in order from newest to oldest
448 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
449
450 return !redfishLogFiles.empty();
451}
452
Johnathan Mantey043a0532020-03-10 17:15:28 -0700453static void ParseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500454 const std::vector<std::pair<std::string, VariantType>>& params,
455 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700456{
457 for (auto property : params)
458 {
459 if (property.first == "Timestamp")
460 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500461 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500462 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700463 if (value != nullptr)
464 {
465 timestamp = *value;
466 }
467 }
468 else if (property.first == "Filename")
469 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500470 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500471 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700472 if (value != nullptr)
473 {
474 filename = *value;
475 }
476 }
477 else if (property.first == "Log")
478 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500479 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500480 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700481 if (value != nullptr)
482 {
483 logfile = *value;
484 }
485 }
486 }
487}
488
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500489constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800490class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700491{
492 public:
493 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500494 SystemLogServiceCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800495 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800496 {
497 entityPrivileges = {
498 {boost::beast::http::verb::get, {{"Login"}}},
499 {boost::beast::http::verb::head, {{"Login"}}},
500 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
501 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
502 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
503 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
504 }
505
506 private:
507 /**
508 * Functions triggers appropriate requests on DBus
509 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500510 void doGet(crow::Response& res, const crow::Request& req,
511 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800512 {
513 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800514 // Collections don't include the static data added by SubRoute because
515 // it has a duplicate entry for members
516 asyncResp->res.jsonValue["@odata.type"] =
517 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800518 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800519 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800520 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
521 asyncResp->res.jsonValue["Description"] =
522 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500523 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800524 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800525 logServiceArray.push_back(
526 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600527#ifdef BMCWEB_ENABLE_REDFISH_SYSTEMDUMP_LOG
528 logServiceArray.push_back(
529 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/System"}});
530#endif
531
Jason M. Billsd53dd412019-02-12 17:16:22 -0800532#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
533 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500534 {{"@odata.id",
535 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800536#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800537 asyncResp->res.jsonValue["Members@odata.count"] =
538 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800539
540 crow::connections::systemBus->async_method_call(
541 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500542 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800543 if (ec)
544 {
545 BMCWEB_LOG_ERROR << ec;
546 return;
547 }
548
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500549 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800550 {
551 if (pathStr.find("PostCode") != std::string::npos)
552 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500553 nlohmann::json& logServiceArray =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800554 asyncResp->res.jsonValue["Members"];
555 logServiceArray.push_back(
556 {{"@odata.id", "/redfish/v1/Systems/system/"
557 "LogServices/PostCodes"}});
558 asyncResp->res.jsonValue["Members@odata.count"] =
559 logServiceArray.size();
560 return;
561 }
562 }
563 },
564 "xyz.openbmc_project.ObjectMapper",
565 "/xyz/openbmc_project/object_mapper",
566 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500567 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800568 }
569};
570
571class EventLogService : public Node
572{
573 public:
574 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500575 EventLogService(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800576 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800577 {
578 entityPrivileges = {
579 {boost::beast::http::verb::get, {{"Login"}}},
580 {boost::beast::http::verb::head, {{"Login"}}},
581 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
582 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
583 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
584 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
585 }
586
587 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500588 void doGet(crow::Response& res, const crow::Request& req,
589 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800590 {
591 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
592
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800593 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800594 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800595 asyncResp->res.jsonValue["@odata.type"] =
596 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800597 asyncResp->res.jsonValue["Name"] = "Event Log Service";
598 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -0500599 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800600 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
601 asyncResp->res.jsonValue["Entries"] = {
602 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800603 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500604 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
605
606 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
607 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700608 }
609};
610
Tim Lee1f56a3a2019-10-09 10:17:57 +0800611class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700612{
613 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500614 JournalEventLogClear(CrowApp& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700615 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
616 "LogService.ClearLog/")
617 {
618 entityPrivileges = {
619 {boost::beast::http::verb::get, {{"Login"}}},
620 {boost::beast::http::verb::head, {{"Login"}}},
621 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
622 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
623 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
624 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
625 }
626
627 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500628 void doPost(crow::Response& res, const crow::Request& req,
629 const std::vector<std::string>& params) override
Jason M. Bills489640c2019-05-17 09:56:36 -0700630 {
631 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
632
633 // Clear the EventLog by deleting the log files
634 std::vector<std::filesystem::path> redfishLogFiles;
635 if (getRedfishLogFiles(redfishLogFiles))
636 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500637 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -0700638 {
639 std::error_code ec;
640 std::filesystem::remove(file, ec);
641 }
642 }
643
644 // Reload rsyslog so it knows to start new log files
645 crow::connections::systemBus->async_method_call(
646 [asyncResp](const boost::system::error_code ec) {
647 if (ec)
648 {
649 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
650 messages::internalError(asyncResp->res);
651 return;
652 }
653
654 messages::success(asyncResp->res);
655 },
656 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
657 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
658 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800659 }
660};
661
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500662static int fillEventLogEntryJson(const std::string& logEntryID,
Jason M. Bills95820182019-04-22 16:25:34 -0700663 const std::string logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500664 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800665{
Jason M. Bills95820182019-04-22 16:25:34 -0700666 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700667 // First get the Timestamp
668 size_t space = logEntry.find_first_of(" ");
669 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700670 {
671 return 1;
672 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700673 std::string timestamp = logEntry.substr(0, space);
674 // Then get the log contents
675 size_t entryStart = logEntry.find_first_not_of(" ", space);
676 if (entryStart == std::string::npos)
677 {
678 return 1;
679 }
680 std::string_view entry(logEntry);
681 entry.remove_prefix(entryStart);
682 // Use split to separate the entry into its fields
683 std::vector<std::string> logEntryFields;
684 boost::split(logEntryFields, entry, boost::is_any_of(","),
685 boost::token_compress_on);
686 // We need at least a MessageId to be valid
687 if (logEntryFields.size() < 1)
688 {
689 return 1;
690 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500691 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700692
Jason M. Bills4851d452019-03-28 11:27:48 -0700693 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500694 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -0700695 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800696
Jason M. Bills4851d452019-03-28 11:27:48 -0700697 std::string msg;
698 std::string severity;
699 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800700 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700701 msg = message->message;
702 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800703 }
704
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700705 // Get the MessageArgs from the log if there are any
706 boost::beast::span<std::string> messageArgs;
707 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700708 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500709 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700710 // If the first string is empty, assume there are no MessageArgs
711 std::size_t messageArgsSize = 0;
712 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700713 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700714 messageArgsSize = logEntryFields.size() - 1;
715 }
716
717 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
718
719 // Fill the MessageArgs into the Message
720 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500721 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700722 {
723 std::string argStr = "%" + std::to_string(++i);
724 size_t argPos = msg.find(argStr);
725 if (argPos != std::string::npos)
726 {
727 msg.replace(argPos, argStr.length(), messageArg);
728 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700729 }
730 }
731
Jason M. Bills95820182019-04-22 16:25:34 -0700732 // Get the Created time from the timestamp. The log timestamp is in RFC3339
733 // format which matches the Redfish format except for the fractional seconds
734 // between the '.' and the '+', so just remove them.
735 std::size_t dot = timestamp.find_first_of(".");
736 std::size_t plus = timestamp.find_first_of("+");
737 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800738 {
Jason M. Bills95820182019-04-22 16:25:34 -0700739 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800740 }
741
742 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700743 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700744 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800745 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700746 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700747 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800748 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700749 {"Id", logEntryID},
750 {"Message", std::move(msg)},
751 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800752 {"MessageArgs", std::move(messageArgs)},
753 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700754 {"Severity", std::move(severity)},
755 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800756 return 0;
757}
758
Anthony Wilson27062602019-04-22 02:10:09 -0500759class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800760{
761 public:
762 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500763 JournalEventLogEntryCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800764 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800765 {
766 entityPrivileges = {
767 {boost::beast::http::verb::get, {{"Login"}}},
768 {boost::beast::http::verb::head, {{"Login"}}},
769 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
770 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
771 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
772 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
773 }
774
775 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500776 void doGet(crow::Response& res, const crow::Request& req,
777 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800778 {
779 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700780 uint64_t skip = 0;
781 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800782 if (!getSkipParam(asyncResp->res, req, skip))
783 {
784 return;
785 }
786 if (!getTopParam(asyncResp->res, req, top))
787 {
788 return;
789 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800790 // Collections don't include the static data added by SubRoute because
791 // it has a duplicate entry for members
792 asyncResp->res.jsonValue["@odata.type"] =
793 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800794 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800795 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800796 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
797 asyncResp->res.jsonValue["Description"] =
798 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700799
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500800 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800801 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700802 // Go through the log files and create a unique ID for each entry
803 std::vector<std::filesystem::path> redfishLogFiles;
804 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000805 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700806 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700807
808 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700809 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
810 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800811 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700812 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700813 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800814 {
815 continue;
816 }
817
Jason M. Billse85d6b12019-07-29 17:01:15 -0700818 // Reset the unique ID on the first entry
819 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700820 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800821 {
Jason M. Bills95820182019-04-22 16:25:34 -0700822 entryCount++;
823 // Handle paging using skip (number of entries to skip from the
824 // start) and top (number of entries to display)
825 if (entryCount <= skip || entryCount > skip + top)
826 {
827 continue;
828 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800829
Jason M. Bills95820182019-04-22 16:25:34 -0700830 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700831 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700832 {
833 continue;
834 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800835
Jason M. Billse85d6b12019-07-29 17:01:15 -0700836 if (firstEntry)
837 {
838 firstEntry = false;
839 }
840
Jason M. Bills95820182019-04-22 16:25:34 -0700841 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500842 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -0700843 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
844 {
845 messages::internalError(asyncResp->res);
846 return;
847 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800848 }
849 }
850 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
851 if (skip + top < entryCount)
852 {
853 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700854 "/redfish/v1/Systems/system/LogServices/EventLog/"
855 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800856 std::to_string(skip + top);
857 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500858 }
859};
860
Jason M. Bills897967d2019-07-29 17:05:30 -0700861class JournalEventLogEntry : public Node
862{
863 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500864 JournalEventLogEntry(CrowApp& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -0700865 Node(app,
866 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
867 std::string())
868 {
869 entityPrivileges = {
870 {boost::beast::http::verb::get, {{"Login"}}},
871 {boost::beast::http::verb::head, {{"Login"}}},
872 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
873 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
874 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
875 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
876 }
877
878 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500879 void doGet(crow::Response& res, const crow::Request& req,
880 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -0700881 {
882 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
883 if (params.size() != 1)
884 {
885 messages::internalError(asyncResp->res);
886 return;
887 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500888 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -0700889
890 // Go through the log files and check the unique ID for each entry to
891 // find the target entry
892 std::vector<std::filesystem::path> redfishLogFiles;
893 getRedfishLogFiles(redfishLogFiles);
894 std::string logEntry;
895
896 // Oldest logs are in the last file, so start there and loop backwards
897 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
898 it++)
899 {
900 std::ifstream logStream(*it);
901 if (!logStream.is_open())
902 {
903 continue;
904 }
905
906 // Reset the unique ID on the first entry
907 bool firstEntry = true;
908 while (std::getline(logStream, logEntry))
909 {
910 std::string idStr;
911 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
912 {
913 continue;
914 }
915
916 if (firstEntry)
917 {
918 firstEntry = false;
919 }
920
921 if (idStr == targetID)
922 {
923 if (fillEventLogEntryJson(idStr, logEntry,
924 asyncResp->res.jsonValue) != 0)
925 {
926 messages::internalError(asyncResp->res);
927 return;
928 }
929 return;
930 }
931 }
932 }
933 // Requested ID was not found
934 messages::resourceMissingAtURI(asyncResp->res, targetID);
935 }
936};
937
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500938class DBusEventLogEntryCollection : public Node
939{
940 public:
941 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500942 DBusEventLogEntryCollection(CrowApp& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500943 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
944 {
945 entityPrivileges = {
946 {boost::beast::http::verb::get, {{"Login"}}},
947 {boost::beast::http::verb::head, {{"Login"}}},
948 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
949 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
950 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
951 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
952 }
953
954 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500955 void doGet(crow::Response& res, const crow::Request& req,
956 const std::vector<std::string>& params) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500957 {
958 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
959
960 // Collections don't include the static data added by SubRoute because
961 // it has a duplicate entry for members
962 asyncResp->res.jsonValue["@odata.type"] =
963 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500964 asyncResp->res.jsonValue["@odata.id"] =
965 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
966 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
967 asyncResp->res.jsonValue["Description"] =
968 "Collection of System Event Log Entries";
969
Andrew Geisslercb92c032018-08-17 07:56:14 -0700970 // DBus implementation of EventLog/Entries
971 // Make call to Logging Service to find all log entry objects
972 crow::connections::systemBus->async_method_call(
973 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500974 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700975 if (ec)
976 {
977 // TODO Handle for specific error code
978 BMCWEB_LOG_ERROR
979 << "getLogEntriesIfaceData resp_handler got error "
980 << ec;
981 messages::internalError(asyncResp->res);
982 return;
983 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500984 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -0700985 asyncResp->res.jsonValue["Members"];
986 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500987 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700988 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500989 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700990 {
991 if (interfaceMap.first !=
992 "xyz.openbmc_project.Logging.Entry")
993 {
994 BMCWEB_LOG_DEBUG << "Bailing early on "
995 << interfaceMap.first;
996 continue;
997 }
998 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500999 nlohmann::json& thisEntry = entriesArray.back();
1000 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001001 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001002 std::string* severity = nullptr;
1003 std::string* message = nullptr;
1004 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001005 {
1006 if (propertyMap.first == "Id")
1007 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001008 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001009 if (id == nullptr)
1010 {
1011 messages::propertyMissing(asyncResp->res,
1012 "Id");
1013 }
1014 }
1015 else if (propertyMap.first == "Timestamp")
1016 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001017 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001018 std::get_if<uint64_t>(&propertyMap.second);
1019 if (millisTimeStamp == nullptr)
1020 {
1021 messages::propertyMissing(asyncResp->res,
1022 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001023 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001024 }
1025 // Retrieve Created property with format:
1026 // yyyy-mm-ddThh:mm:ss
1027 std::chrono::milliseconds chronoTimeStamp(
1028 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001029 timestamp = std::chrono::duration_cast<
1030 std::chrono::duration<int>>(
1031 chronoTimeStamp)
1032 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001033 }
1034 else if (propertyMap.first == "Severity")
1035 {
1036 severity = std::get_if<std::string>(
1037 &propertyMap.second);
1038 if (severity == nullptr)
1039 {
1040 messages::propertyMissing(asyncResp->res,
1041 "Severity");
1042 }
1043 }
1044 else if (propertyMap.first == "Message")
1045 {
1046 message = std::get_if<std::string>(
1047 &propertyMap.second);
1048 if (message == nullptr)
1049 {
1050 messages::propertyMissing(asyncResp->res,
1051 "Message");
1052 }
1053 }
1054 }
1055 thisEntry = {
1056 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001057 {"@odata.id",
1058 "/redfish/v1/Systems/system/LogServices/EventLog/"
1059 "Entries/" +
1060 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001061 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001062 {"Id", std::to_string(*id)},
1063 {"Message", *message},
1064 {"EntryType", "Event"},
1065 {"Severity",
1066 translateSeverityDbusToRedfish(*severity)},
1067 {"Created", crow::utility::getDateTime(timestamp)}};
1068 }
1069 }
1070 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001071 [](const nlohmann::json& left,
1072 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001073 return (left["Id"] <= right["Id"]);
1074 });
1075 asyncResp->res.jsonValue["Members@odata.count"] =
1076 entriesArray.size();
1077 },
1078 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1079 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001080 }
1081};
1082
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001083class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001084{
1085 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001086 DBusEventLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001087 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001088 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1089 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001090 {
1091 entityPrivileges = {
1092 {boost::beast::http::verb::get, {{"Login"}}},
1093 {boost::beast::http::verb::head, {{"Login"}}},
1094 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1095 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1096 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1097 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1098 }
1099
1100 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001101 void doGet(crow::Response& res, const crow::Request& req,
1102 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001103 {
1104 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001105 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001106 {
1107 messages::internalError(asyncResp->res);
1108 return;
1109 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001110 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001111
Andrew Geisslercb92c032018-08-17 07:56:14 -07001112 // DBus implementation of EventLog/Entries
1113 // Make call to Logging Service to find all log entry objects
1114 crow::connections::systemBus->async_method_call(
1115 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001116 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001117 if (ec)
1118 {
1119 BMCWEB_LOG_ERROR
1120 << "EventLogEntry (DBus) resp_handler got error " << ec;
1121 messages::internalError(asyncResp->res);
1122 return;
1123 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001124 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001125 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001126 std::string* severity = nullptr;
1127 std::string* message = nullptr;
1128 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001129 {
1130 if (propertyMap.first == "Id")
1131 {
1132 id = std::get_if<uint32_t>(&propertyMap.second);
1133 if (id == nullptr)
1134 {
1135 messages::propertyMissing(asyncResp->res, "Id");
1136 }
1137 }
1138 else if (propertyMap.first == "Timestamp")
1139 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001140 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001141 std::get_if<uint64_t>(&propertyMap.second);
1142 if (millisTimeStamp == nullptr)
1143 {
1144 messages::propertyMissing(asyncResp->res,
1145 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001146 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001147 }
1148 // Retrieve Created property with format:
1149 // yyyy-mm-ddThh:mm:ss
1150 std::chrono::milliseconds chronoTimeStamp(
1151 *millisTimeStamp);
1152 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001153 std::chrono::duration_cast<
1154 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001155 .count();
1156 }
1157 else if (propertyMap.first == "Severity")
1158 {
1159 severity =
1160 std::get_if<std::string>(&propertyMap.second);
1161 if (severity == nullptr)
1162 {
1163 messages::propertyMissing(asyncResp->res,
1164 "Severity");
1165 }
1166 }
1167 else if (propertyMap.first == "Message")
1168 {
1169 message = std::get_if<std::string>(&propertyMap.second);
1170 if (message == nullptr)
1171 {
1172 messages::propertyMissing(asyncResp->res,
1173 "Message");
1174 }
1175 }
1176 }
Ed Tanous271584a2019-07-09 16:24:22 -07001177 if (id == nullptr || message == nullptr || severity == nullptr)
1178 {
1179 return;
1180 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001181 asyncResp->res.jsonValue = {
1182 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001183 {"@odata.id",
1184 "/redfish/v1/Systems/system/LogServices/EventLog/"
1185 "Entries/" +
1186 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001187 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001188 {"Id", std::to_string(*id)},
1189 {"Message", *message},
1190 {"EntryType", "Event"},
1191 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001192 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001193 },
1194 "xyz.openbmc_project.Logging",
1195 "/xyz/openbmc_project/logging/entry/" + entryID,
1196 "org.freedesktop.DBus.Properties", "GetAll",
1197 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001198 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001199
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001200 void doDelete(crow::Response& res, const crow::Request& req,
1201 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001202 {
1203
1204 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1205
1206 auto asyncResp = std::make_shared<AsyncResp>(res);
1207
1208 if (params.size() != 1)
1209 {
1210 messages::internalError(asyncResp->res);
1211 return;
1212 }
1213 std::string entryID = params[0];
1214
1215 dbus::utility::escapePathForDbus(entryID);
1216
1217 // Process response from Logging service.
1218 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1219 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1220 if (ec)
1221 {
1222 // TODO Handle for specific error code
1223 BMCWEB_LOG_ERROR
1224 << "EventLogEntry (DBus) doDelete respHandler got error "
1225 << ec;
1226 asyncResp->res.result(
1227 boost::beast::http::status::internal_server_error);
1228 return;
1229 }
1230
1231 asyncResp->res.result(boost::beast::http::status::ok);
1232 };
1233
1234 // Make call to Logging service to request Delete Log
1235 crow::connections::systemBus->async_method_call(
1236 respHandler, "xyz.openbmc_project.Logging",
1237 "/xyz/openbmc_project/logging/entry/" + entryID,
1238 "xyz.openbmc_project.Object.Delete", "Delete");
1239 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001240};
1241
1242class BMCLogServiceCollection : public Node
1243{
1244 public:
1245 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001246 BMCLogServiceCollection(CrowApp& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001247 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001248 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001249 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001250 {boost::beast::http::verb::get, {{"Login"}}},
1251 {boost::beast::http::verb::head, {{"Login"}}},
1252 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1253 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1254 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1255 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001256 }
1257
1258 private:
1259 /**
1260 * Functions triggers appropriate requests on DBus
1261 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001262 void doGet(crow::Response& res, const crow::Request& req,
1263 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001264 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001265 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001266 // Collections don't include the static data added by SubRoute because
1267 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001268 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001269 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001270 asyncResp->res.jsonValue["@odata.id"] =
1271 "/redfish/v1/Managers/bmc/LogServices";
1272 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1273 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001274 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001275 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001276 logServiceArray = nlohmann::json::array();
1277#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1278 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001279 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001280#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001281 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001282 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001283 }
1284};
1285
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001286class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001287{
1288 public:
1289 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001290 BMCJournalLogService(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001291 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001292 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001293 entityPrivileges = {
1294 {boost::beast::http::verb::get, {{"Login"}}},
1295 {boost::beast::http::verb::head, {{"Login"}}},
1296 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1297 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1298 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1299 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1300 }
1301
1302 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001303 void doGet(crow::Response& res, const crow::Request& req,
1304 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001305 {
1306 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001307 asyncResp->res.jsonValue["@odata.type"] =
1308 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001309 asyncResp->res.jsonValue["@odata.id"] =
1310 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001311 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1312 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1313 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001314 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001315 asyncResp->res.jsonValue["Entries"] = {
1316 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001317 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001318 }
1319};
1320
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001321static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1322 sd_journal* journal,
1323 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001324{
1325 // Get the Log Entry contents
1326 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001327
Ed Tanous39e77502019-03-04 17:35:53 -08001328 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001329 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001330 if (ret < 0)
1331 {
1332 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1333 return 1;
1334 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001335
1336 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001337 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001338 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001339 if (ret < 0)
1340 {
1341 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001342 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001343
1344 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001345 std::string entryTimeStr;
1346 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001347 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001348 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001349 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001350
1351 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001352 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001353 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001354 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1355 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001356 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001357 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001358 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001359 {"EntryType", "Oem"},
1360 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001361 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001362 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001363 {"Created", std::move(entryTimeStr)}};
1364 return 0;
1365}
1366
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001367class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001368{
1369 public:
1370 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001371 BMCJournalLogEntryCollection(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001372 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001373 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001374 entityPrivileges = {
1375 {boost::beast::http::verb::get, {{"Login"}}},
1376 {boost::beast::http::verb::head, {{"Login"}}},
1377 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1378 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1379 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1380 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1381 }
1382
1383 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001384 void doGet(crow::Response& res, const crow::Request& req,
1385 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001386 {
1387 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001388 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001389 uint64_t skip = 0;
1390 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001391 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001392 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001393 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001394 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001395 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001396 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001397 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001398 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001399 // Collections don't include the static data added by SubRoute because
1400 // it has a duplicate entry for members
1401 asyncResp->res.jsonValue["@odata.type"] =
1402 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001403 asyncResp->res.jsonValue["@odata.id"] =
1404 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001405 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001406 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001407 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1408 asyncResp->res.jsonValue["Description"] =
1409 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001410 asyncResp->res.jsonValue["@odata.id"] =
1411 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001412 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001413 logEntryArray = nlohmann::json::array();
1414
1415 // Go through the journal and use the timestamp to create a unique ID
1416 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001417 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001418 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1419 if (ret < 0)
1420 {
1421 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001422 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001423 return;
1424 }
1425 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1426 journalTmp, sd_journal_close);
1427 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001428 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001429 // Reset the unique ID on the first entry
1430 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001431 SD_JOURNAL_FOREACH(journal.get())
1432 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001433 entryCount++;
1434 // Handle paging using skip (number of entries to skip from the
1435 // start) and top (number of entries to display)
1436 if (entryCount <= skip || entryCount > skip + top)
1437 {
1438 continue;
1439 }
1440
Jason M. Bills16428a12018-11-02 12:42:29 -07001441 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001442 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001443 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001444 continue;
1445 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001446
Jason M. Billse85d6b12019-07-29 17:01:15 -07001447 if (firstEntry)
1448 {
1449 firstEntry = false;
1450 }
1451
Jason M. Billse1f26342018-07-18 12:12:00 -07001452 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001453 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001454 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1455 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001456 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001457 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001458 return;
1459 }
1460 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001461 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1462 if (skip + top < entryCount)
1463 {
1464 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001465 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001466 std::to_string(skip + top);
1467 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001468 }
1469};
1470
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001471class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001472{
1473 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001474 BMCJournalLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001475 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001476 std::string())
1477 {
1478 entityPrivileges = {
1479 {boost::beast::http::verb::get, {{"Login"}}},
1480 {boost::beast::http::verb::head, {{"Login"}}},
1481 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1482 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1483 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1484 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1485 }
1486
1487 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001488 void doGet(crow::Response& res, const crow::Request& req,
1489 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001490 {
1491 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1492 if (params.size() != 1)
1493 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001494 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001495 return;
1496 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001497 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001498 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001499 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001500 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001501 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001502 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001503 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001504 }
1505
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001506 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001507 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1508 if (ret < 0)
1509 {
1510 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001511 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001512 return;
1513 }
1514 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1515 journalTmp, sd_journal_close);
1516 journalTmp = nullptr;
1517 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001518 // tracking the unique ID
1519 std::string idStr;
1520 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001521 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301522 if (ret < 0)
1523 {
1524 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1525 << strerror(-ret);
1526 messages::internalError(asyncResp->res);
1527 return;
1528 }
Ed Tanous271584a2019-07-09 16:24:22 -07001529 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001530 {
1531 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001532 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1533 {
1534 messages::internalError(asyncResp->res);
1535 return;
1536 }
1537 if (firstEntry)
1538 {
1539 firstEntry = false;
1540 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001541 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001542 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001543 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001544 {
1545 messages::resourceMissingAtURI(asyncResp->res, entryID);
1546 return;
1547 }
1548
1549 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1550 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001551 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001552 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001553 return;
1554 }
1555 }
1556};
1557
raviteja-bc9bb6862020-02-03 11:53:32 -06001558class SystemDumpService : public Node
1559{
1560 public:
1561 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001562 SystemDumpService(CrowApp& app) :
raviteja-bc9bb6862020-02-03 11:53:32 -06001563 Node(app, "/redfish/v1/Systems/system/LogServices/System/")
1564 {
1565 entityPrivileges = {
1566 {boost::beast::http::verb::get, {{"Login"}}},
1567 {boost::beast::http::verb::head, {{"Login"}}},
1568 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1569 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1570 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1571 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1572 }
1573
1574 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001575 void doGet(crow::Response& res, const crow::Request& req,
1576 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001577 {
1578 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1579
1580 asyncResp->res.jsonValue["@odata.id"] =
1581 "/redfish/v1/Systems/system/LogServices/System";
1582 asyncResp->res.jsonValue["@odata.type"] =
1583 "#LogService.v1_1_0.LogService";
1584 asyncResp->res.jsonValue["Name"] = "Dump Log Service";
1585 asyncResp->res.jsonValue["Description"] = "System Dump Log Service";
1586 asyncResp->res.jsonValue["Id"] = "System";
1587 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1588 asyncResp->res.jsonValue["LogEntryTypes"] = "Dump";
1589 asyncResp->res.jsonValue["Oem"]["DumpType"] = "System";
1590
1591 asyncResp->res.jsonValue["Entries"] = {
1592 {"@odata.id",
1593 "/redfish/v1/Systems/system/LogServices/System/Entries"}};
1594 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1595 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1596 "Actions/LogService.ClearLog"}};
1597 asyncResp->res.jsonValue["Actions"]["#LogService.CreateLog"] = {
1598 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1599 "Actions/LogService.CreateLog"}};
1600 }
1601};
1602
1603class SystemDumpEntryCollection : public Node
1604{
1605 public:
1606 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001607 SystemDumpEntryCollection(CrowApp& app) :
raviteja-bc9bb6862020-02-03 11:53:32 -06001608 Node(app, "/redfish/v1/Systems/system/LogServices/System/Entries/")
1609 {
1610 entityPrivileges = {
1611 {boost::beast::http::verb::get, {{"Login"}}},
1612 {boost::beast::http::verb::head, {{"Login"}}},
1613 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1614 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1615 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1616 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1617 }
1618
1619 private:
1620 /**
1621 * Functions triggers appropriate requests on DBus
1622 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001623 void doGet(crow::Response& res, const crow::Request& req,
1624 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001625 {
1626 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1627
1628 asyncResp->res.jsonValue["@odata.type"] =
1629 "#LogEntryCollection.LogEntryCollection";
1630 asyncResp->res.jsonValue["@odata.id"] =
1631 "/redfish/v1/Systems/system/LogServices/System/Entries";
1632 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
1633 asyncResp->res.jsonValue["Description"] =
1634 "Collection of System Dump Entries";
1635
1636 crow::connections::systemBus->async_method_call(
1637 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001638 const crow::openbmc_mapper::GetSubTreeType& resp) {
raviteja-bc9bb6862020-02-03 11:53:32 -06001639 if (ec)
1640 {
1641 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1642 messages::internalError(asyncResp->res);
1643 return;
1644 }
1645
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001646 nlohmann::json& logArray = asyncResp->res.jsonValue["Members"];
raviteja-bc9bb6862020-02-03 11:53:32 -06001647 logArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001648 for (auto& object : resp)
raviteja-bc9bb6862020-02-03 11:53:32 -06001649 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001650 const std::string& path =
1651 static_cast<const std::string&>(object.first);
raviteja-bc9bb6862020-02-03 11:53:32 -06001652 std::size_t lastPos = path.rfind("/");
1653 if (lastPos == std::string::npos)
1654 {
1655 continue;
1656 }
1657 std::string logID = path.substr(lastPos + 1);
1658 logArray.push_back(
1659 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1660 "System/Entries/" +
1661 logID}});
1662 }
1663 asyncResp->res.jsonValue["Members@odata.count"] =
1664 logArray.size();
1665 },
1666 "xyz.openbmc_project.ObjectMapper",
1667 "/xyz/openbmc_project/object_mapper",
1668 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1669 "/xyz/openbmc_project/dump", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001670 std::array<const char*, 1>{
raviteja-bc9bb6862020-02-03 11:53:32 -06001671 "xyz.openbmc_project.Dump.Entry.System"});
1672 }
1673};
1674
1675class SystemDumpEntry : public Node
1676{
1677 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001678 SystemDumpEntry(CrowApp& app) :
raviteja-bc9bb6862020-02-03 11:53:32 -06001679 Node(app,
1680 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/",
1681 std::string())
1682 {
1683 entityPrivileges = {
1684 {boost::beast::http::verb::get, {{"Login"}}},
1685 {boost::beast::http::verb::head, {{"Login"}}},
1686 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1687 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1688 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1689 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1690 }
1691
1692 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001693 void doGet(crow::Response& res, const crow::Request& req,
1694 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001695 {
1696 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1697 if (params.size() != 1)
1698 {
1699 messages::internalError(asyncResp->res);
1700 return;
1701 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001702 const std::string& entryID = params[0];
raviteja-bc9bb6862020-02-03 11:53:32 -06001703 crow::connections::systemBus->async_method_call(
1704 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001705 GetManagedObjectsType& resp) {
raviteja-bc9bb6862020-02-03 11:53:32 -06001706 if (ec)
1707 {
1708 BMCWEB_LOG_ERROR
1709 << "SystemDumpEntry resp_handler got error " << ec;
1710 messages::internalError(asyncResp->res);
1711 return;
1712 }
1713
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001714 for (auto& objectPath : resp)
raviteja-bc9bb6862020-02-03 11:53:32 -06001715 {
1716 if (objectPath.first.str.find(
1717 "/xyz/openbmc_project/dump/entry/" + entryID) ==
1718 std::string::npos)
1719 {
1720 continue;
1721 }
1722
1723 bool foundSystemDumpEntry = false;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001724 for (auto& interfaceMap : objectPath.second)
raviteja-bc9bb6862020-02-03 11:53:32 -06001725 {
1726 if (interfaceMap.first ==
1727 "xyz.openbmc_project.Dump.Entry.System")
1728 {
1729 foundSystemDumpEntry = true;
1730 break;
1731 }
1732 }
1733 if (foundSystemDumpEntry == false)
1734 {
1735 BMCWEB_LOG_DEBUG << "Can't find System Dump Entry";
1736 messages::internalError(asyncResp->res);
1737 return;
1738 }
1739
1740 std::string timestamp{};
1741 uint64_t size = 0;
1742
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001743 for (auto& interfaceMap : objectPath.second)
raviteja-bc9bb6862020-02-03 11:53:32 -06001744 {
1745 if (interfaceMap.first ==
1746 "xyz.openbmc_project.Dump.Entry")
1747 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001748 for (auto& propertyMap : interfaceMap.second)
raviteja-bc9bb6862020-02-03 11:53:32 -06001749 {
1750 if (propertyMap.first == "Size")
1751 {
1752 auto sizePtr = std::get_if<uint64_t>(
1753 &propertyMap.second);
1754 if (sizePtr == nullptr)
1755 {
1756 messages::propertyMissing(
1757 asyncResp->res, "Size");
1758 break;
1759 }
1760 size = *sizePtr;
1761 break;
1762 }
1763 }
1764 }
1765 else if (interfaceMap.first ==
1766 "xyz.openbmc_project.Time.EpochTime")
1767 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001768 for (auto& propertyMap : interfaceMap.second)
raviteja-bc9bb6862020-02-03 11:53:32 -06001769 {
1770 if (propertyMap.first == "Elapsed")
1771 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001772 const uint64_t* usecsTimeStamp =
raviteja-bc9bb6862020-02-03 11:53:32 -06001773 std::get_if<uint64_t>(
1774 &propertyMap.second);
1775 if (usecsTimeStamp == nullptr)
1776 {
1777 messages::propertyMissing(
1778 asyncResp->res, "Elapsed");
1779 break;
1780 }
1781 getTimestampStr(*usecsTimeStamp, timestamp);
1782 break;
1783 }
1784 }
1785 }
1786 }
1787 asyncResp->res.jsonValue = {
1788 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1789 {"@odata.id",
1790 "/redfish/v1/Systems/system/LogServices/System/"
1791 "Entries/" +
1792 entryID},
1793 {"Name", "System Dump Entry"},
1794 {"Id", entryID},
1795 {"SizeInB", size},
1796 {"EntryType", "Dump"},
1797 {"EntryCode", "User generated dump"},
1798 {"Created", timestamp}};
1799
1800 asyncResp->res
1801 .jsonValue["Actions"]["#LogEntry.DownloadLog"] = {
1802 {"target",
1803 "/redfish/v1/Systems/system/LogServices/System/"
1804 "Entries/" +
1805 entryID + "/Actions/LogEntry.DownloadLog"}};
1806 }
1807 },
1808 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
1809 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1810 }
1811
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001812 void doDelete(crow::Response& res, const crow::Request& req,
1813 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001814 {
1815 BMCWEB_LOG_DEBUG << "Do delete single dump entry";
1816
1817 auto asyncResp = std::make_shared<AsyncResp>(res);
1818
1819 if (params.size() != 1)
1820 {
1821 messages::internalError(asyncResp->res);
1822 return;
1823 }
1824 std::string entryID = params[0];
1825
1826 crow::connections::systemBus->async_method_call(
1827 [asyncResp,
1828 entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001829 const crow::openbmc_mapper::GetSubTreeType& resp) {
raviteja-bc9bb6862020-02-03 11:53:32 -06001830 if (ec)
1831 {
1832 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1833 messages::internalError(asyncResp->res);
1834 return;
1835 }
1836
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001837 for (auto& object : resp)
raviteja-bc9bb6862020-02-03 11:53:32 -06001838 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001839 const std::string& path =
1840 static_cast<const std::string&>(object.first);
raviteja-bc9bb6862020-02-03 11:53:32 -06001841
1842 std::size_t pos = path.rfind(
1843 "/xyz/openbmc_project/dump/entry/" + entryID);
1844 if (pos != std::string::npos)
1845 {
1846 deleteSystemDumpEntry(asyncResp->res, entryID);
1847 return;
1848 }
1849 }
1850 },
1851 "xyz.openbmc_project.ObjectMapper",
1852 "/xyz/openbmc_project/object_mapper",
1853 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1854 "/xyz/openbmc_project/dump", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001855 std::array<const char*, 1>{
raviteja-bc9bb6862020-02-03 11:53:32 -06001856 "xyz.openbmc_project.Dump.Entry.System"});
1857 }
1858};
1859
raviteja-b06578432020-02-03 12:50:42 -06001860class SystemDumpEntryDownload : public Node
1861{
1862 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001863 SystemDumpEntryDownload(CrowApp& app) :
raviteja-b06578432020-02-03 12:50:42 -06001864 Node(app,
1865 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/"
1866 "Actions/"
1867 "LogEntry.DownloadLog/",
1868 std::string())
1869 {
1870 entityPrivileges = {
1871 {boost::beast::http::verb::get, {{"Login"}}},
1872 {boost::beast::http::verb::head, {{"Login"}}},
1873 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1874 }
1875
1876 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001877 void doPost(crow::Response& res, const crow::Request& req,
1878 const std::vector<std::string>& params) override
raviteja-b06578432020-02-03 12:50:42 -06001879 {
1880 if (params.size() != 1)
1881 {
1882 messages::internalError(res);
1883 return;
1884 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001885 const std::string& entryID = params[0];
raviteja-b06578432020-02-03 12:50:42 -06001886 crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID);
1887 }
1888};
1889
raviteja-b013487e2020-03-03 03:20:48 -06001890class SystemDumpClear : public Node
1891{
1892 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001893 SystemDumpClear(CrowApp& app) :
raviteja-b013487e2020-03-03 03:20:48 -06001894 Node(app, "/redfish/v1/Systems/system/LogServices/System/"
1895 "Actions/"
1896 "LogService.ClearLog/")
1897 {
1898 entityPrivileges = {
1899 {boost::beast::http::verb::get, {{"Login"}}},
1900 {boost::beast::http::verb::head, {{"Login"}}},
1901 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1902 }
1903
1904 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001905 void doPost(crow::Response& res, const crow::Request& req,
1906 const std::vector<std::string>& params) override
raviteja-b013487e2020-03-03 03:20:48 -06001907 {
1908
1909 auto asyncResp = std::make_shared<AsyncResp>(res);
1910 crow::connections::systemBus->async_method_call(
1911 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001912 const std::vector<std::string>& dumpList) {
raviteja-b013487e2020-03-03 03:20:48 -06001913 if (ec)
1914 {
1915 messages::internalError(asyncResp->res);
1916 return;
1917 }
1918
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001919 for (const std::string& objectPath : dumpList)
raviteja-b013487e2020-03-03 03:20:48 -06001920 {
1921 std::size_t pos = objectPath.rfind("/");
1922 if (pos != std::string::npos)
1923 {
1924 std::string logID = objectPath.substr(pos + 1);
1925 deleteSystemDumpEntry(asyncResp->res, logID);
1926 }
1927 }
1928 },
1929 "xyz.openbmc_project.ObjectMapper",
1930 "/xyz/openbmc_project/object_mapper",
1931 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1932 "/xyz/openbmc_project/dump", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001933 std::array<const char*, 1>{
raviteja-b013487e2020-03-03 03:20:48 -06001934 "xyz.openbmc_project.Dump.Entry.System"});
1935 }
1936};
1937
Jason M. Bills424c4172019-03-21 13:50:33 -07001938class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001939{
1940 public:
1941 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001942 CrashdumpService(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001943 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001944 {
AppaRao Puli39460282020-04-07 17:03:04 +05301945 // Note: Deviated from redfish privilege registry for GET & HEAD
1946 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001947 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301948 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1949 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001950 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1951 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1952 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1953 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001954 }
1955
1956 private:
1957 /**
1958 * Functions triggers appropriate requests on DBus
1959 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001960 void doGet(crow::Response& res, const crow::Request& req,
1961 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001962 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001963 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001964 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001965 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001966 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001967 asyncResp->res.jsonValue["@odata.type"] =
1968 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001969 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1970 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1971 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001972 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1973 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001974 asyncResp->res.jsonValue["Entries"] = {
1975 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001976 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001977 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001978 {"#LogService.ClearLog",
1979 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1980 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001981 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001982 {{"#Crashdump.OnDemand",
1983 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
Kenny L. Ku6eda7682020-06-19 09:48:36 -07001984 "Actions/Oem/Crashdump.OnDemand"}}},
1985 {"#Crashdump.Telemetry",
1986 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1987 "Actions/Oem/Crashdump.Telemetry"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001988
1989#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001990 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001991 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001992 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1993 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001994#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001995 }
1996};
1997
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001998class CrashdumpClear : public Node
1999{
2000 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002001 CrashdumpClear(CrowApp& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002002 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
2003 "LogService.ClearLog/")
2004 {
AppaRao Puli39460282020-04-07 17:03:04 +05302005 // Note: Deviated from redfish privilege registry for GET & HEAD
2006 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002007 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302008 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2009 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002010 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2011 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2012 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2013 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2014 }
2015
2016 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002017 void doPost(crow::Response& res, const crow::Request& req,
2018 const std::vector<std::string>& params) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002019 {
2020 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2021
2022 crow::connections::systemBus->async_method_call(
2023 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002024 const std::string& resp) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002025 if (ec)
2026 {
2027 messages::internalError(asyncResp->res);
2028 return;
2029 }
2030 messages::success(asyncResp->res);
2031 },
2032 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2033 }
2034};
2035
Jason M. Billse855dd22019-10-08 11:37:48 -07002036static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002037 const std::string& logID,
2038 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002039{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002040 auto getStoredLogCallback =
2041 [asyncResp, logID, &logEntryJson](
2042 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002043 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002044 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002045 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002046 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2047 if (ec.value() ==
2048 boost::system::linux_error::bad_request_descriptor)
2049 {
2050 messages::resourceNotFound(asyncResp->res, "LogEntry",
2051 logID);
2052 }
2053 else
2054 {
2055 messages::internalError(asyncResp->res);
2056 }
2057 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002058 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002059
Johnathan Mantey043a0532020-03-10 17:15:28 -07002060 std::string timestamp{};
2061 std::string filename{};
2062 std::string logfile{};
2063 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2064
2065 if (filename.empty() || timestamp.empty())
2066 {
2067 messages::resourceMissingAtURI(asyncResp->res, logID);
2068 return;
2069 }
2070
2071 std::string crashdumpURI =
2072 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2073 logID + "/" + filename;
2074 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2075 {"@odata.id", "/redfish/v1/Systems/system/"
2076 "LogServices/Crashdump/Entries/" +
2077 logID},
2078 {"Name", "CPU Crashdump"},
2079 {"Id", logID},
2080 {"EntryType", "Oem"},
2081 {"OemRecordFormat", "Crashdump URI"},
2082 {"Message", std::move(crashdumpURI)},
2083 {"Created", std::move(timestamp)}};
2084 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002085 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002086 std::move(getStoredLogCallback), crashdumpObject,
2087 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002088 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002089}
2090
Jason M. Bills424c4172019-03-21 13:50:33 -07002091class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002092{
2093 public:
2094 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002095 CrashdumpEntryCollection(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002096 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002097 {
AppaRao Puli39460282020-04-07 17:03:04 +05302098 // Note: Deviated from redfish privilege registry for GET & HEAD
2099 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002100 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302101 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2102 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002103 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2104 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2105 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2106 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002107 }
2108
2109 private:
2110 /**
2111 * Functions triggers appropriate requests on DBus
2112 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002113 void doGet(crow::Response& res, const crow::Request& req,
2114 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002115 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002116 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002117 // Collections don't include the static data added by SubRoute because
2118 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002119 auto getLogEntriesCallback = [asyncResp](
2120 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002121 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002122 if (ec)
2123 {
2124 if (ec.value() !=
2125 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002126 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002127 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2128 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002129 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002130 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002131 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002132 }
2133 asyncResp->res.jsonValue["@odata.type"] =
2134 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002135 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002136 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002137 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002138 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002139 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002140 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002141 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002142 std::vector<std::string> logIDs;
2143 // Get the list of log entries and build up an empty array big
2144 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002145 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002146 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002147 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002148 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002149 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002150 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002151 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002152 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002153 logIDs.emplace_back(objpath.substr(lastPos + 1));
2154
2155 // Add a space for the log entry to the array
2156 logEntryArray.push_back({});
2157 }
2158 // Now go through and set up async calls to fill in the entries
2159 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002160 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002161 {
2162 // Add the log entry to the array
2163 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002164 }
2165 asyncResp->res.jsonValue["Members@odata.count"] =
2166 logEntryArray.size();
2167 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002168 crow::connections::systemBus->async_method_call(
2169 std::move(getLogEntriesCallback),
2170 "xyz.openbmc_project.ObjectMapper",
2171 "/xyz/openbmc_project/object_mapper",
2172 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002173 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002174 }
2175};
2176
Jason M. Bills424c4172019-03-21 13:50:33 -07002177class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002178{
2179 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002180 CrashdumpEntry(CrowApp& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002181 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002182 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002183 std::string())
2184 {
AppaRao Puli39460282020-04-07 17:03:04 +05302185 // Note: Deviated from redfish privilege registry for GET & HEAD
2186 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002187 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302188 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2189 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002190 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2191 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2192 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2193 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002194 }
2195
2196 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002197 void doGet(crow::Response& res, const crow::Request& req,
2198 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002199 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002200 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002201 if (params.size() != 1)
2202 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002203 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002204 return;
2205 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002206 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002207 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2208 }
2209};
2210
2211class CrashdumpFile : public Node
2212{
2213 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002214 CrashdumpFile(CrowApp& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002215 Node(app,
2216 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2217 "<str>/",
2218 std::string(), std::string())
2219 {
AppaRao Puli39460282020-04-07 17:03:04 +05302220 // Note: Deviated from redfish privilege registry for GET & HEAD
2221 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002222 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302223 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2224 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002225 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2226 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2227 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2228 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2229 }
2230
2231 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002232 void doGet(crow::Response& res, const crow::Request& req,
2233 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002234 {
2235 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2236 if (params.size() != 2)
2237 {
2238 messages::internalError(asyncResp->res);
2239 return;
2240 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002241 const std::string& logID = params[0];
2242 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002243
Johnathan Mantey043a0532020-03-10 17:15:28 -07002244 auto getStoredLogCallback =
2245 [asyncResp, logID, fileName](
2246 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002247 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002248 if (ec)
2249 {
2250 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2251 << ec.message();
2252 messages::internalError(asyncResp->res);
2253 return;
2254 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002255
Johnathan Mantey043a0532020-03-10 17:15:28 -07002256 std::string dbusFilename{};
2257 std::string dbusTimestamp{};
2258 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002259
Johnathan Mantey043a0532020-03-10 17:15:28 -07002260 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2261 dbusFilepath);
2262
2263 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2264 dbusFilepath.empty())
2265 {
2266 messages::resourceMissingAtURI(asyncResp->res, fileName);
2267 return;
2268 }
2269
2270 // Verify the file name parameter is correct
2271 if (fileName != dbusFilename)
2272 {
2273 messages::resourceMissingAtURI(asyncResp->res, fileName);
2274 return;
2275 }
2276
2277 if (!std::filesystem::exists(dbusFilepath))
2278 {
2279 messages::resourceMissingAtURI(asyncResp->res, fileName);
2280 return;
2281 }
2282 std::ifstream ifs(dbusFilepath, std::ios::in |
2283 std::ios::binary |
2284 std::ios::ate);
2285 std::ifstream::pos_type fileSize = ifs.tellg();
2286 if (fileSize < 0)
2287 {
2288 messages::generalError(asyncResp->res);
2289 return;
2290 }
2291 ifs.seekg(0, std::ios::beg);
2292
2293 auto crashData = std::make_unique<char[]>(
2294 static_cast<unsigned int>(fileSize));
2295
2296 ifs.read(crashData.get(), static_cast<int>(fileSize));
2297
2298 // The cast to std::string is intentional in order to use the
2299 // assign() that applies move mechanics
2300 asyncResp->res.body().assign(
2301 static_cast<std::string>(crashData.get()));
2302
2303 // Configure this to be a file download when accessed from
2304 // a browser
2305 asyncResp->res.addHeader("Content-Disposition", "attachment");
2306 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002307 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002308 std::move(getStoredLogCallback), crashdumpObject,
2309 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002310 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002311 }
2312};
2313
Jason M. Bills424c4172019-03-21 13:50:33 -07002314class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002315{
2316 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002317 OnDemandCrashdump(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002318 Node(app,
2319 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2320 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002321 {
AppaRao Puli39460282020-04-07 17:03:04 +05302322 // Note: Deviated from redfish privilege registry for GET & HEAD
2323 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002324 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302325 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2326 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2327 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2328 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2329 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2330 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002331 }
2332
2333 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002334 void doPost(crow::Response& res, const crow::Request& req,
2335 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002336 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002337 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002338
James Feistfe306722020-03-12 16:32:08 -07002339 auto generateonDemandLogCallback = [asyncResp,
2340 req](const boost::system::error_code
2341 ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002342 const std::string& resp) {
James Feist46229572020-02-19 15:11:58 -08002343 if (ec)
2344 {
2345 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002346 {
James Feist46229572020-02-19 15:11:58 -08002347 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002348 }
James Feist46229572020-02-19 15:11:58 -08002349 else if (ec.value() ==
2350 boost::system::errc::device_or_resource_busy)
2351 {
2352 messages::serviceTemporarilyUnavailable(asyncResp->res,
2353 "60");
2354 }
2355 else
2356 {
2357 messages::internalError(asyncResp->res);
2358 }
2359 return;
2360 }
2361 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002362 [](boost::system::error_code err, sdbusplus::message::message&,
2363 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002364 if (!err)
2365 {
James Feiste5d50062020-05-11 17:29:00 -07002366 taskData->messages.emplace_back(
2367 messages::taskCompletedOK(
2368 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002369 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002370 }
James Feist32898ce2020-03-10 16:16:52 -07002371 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002372 },
James Feist46229572020-02-19 15:11:58 -08002373 "type='signal',interface='org.freedesktop.DBus.Properties',"
2374 "member='PropertiesChanged',arg0namespace='com.intel."
2375 "crashdump'");
2376 task->startTimer(std::chrono::minutes(5));
2377 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002378 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002379 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002380 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002381 std::move(generateonDemandLogCallback), crashdumpObject,
2382 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002383 }
2384};
2385
Kenny L. Ku6eda7682020-06-19 09:48:36 -07002386class TelemetryCrashdump : public Node
2387{
2388 public:
2389 TelemetryCrashdump(CrowApp& app) :
2390 Node(app,
2391 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2392 "Crashdump.Telemetry/")
2393 {
2394 // Note: Deviated from redfish privilege registry for GET & HEAD
2395 // method for security reasons.
2396 entityPrivileges = {
2397 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2398 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2399 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2400 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2401 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2402 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2403 }
2404
2405 private:
2406 void doPost(crow::Response& res, const crow::Request& req,
2407 const std::vector<std::string>& params) override
2408 {
2409 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2410
2411 auto generateTelemetryLogCallback = [asyncResp, req](
2412 const boost::system::error_code
2413 ec,
2414 const std::string& resp) {
2415 if (ec)
2416 {
2417 if (ec.value() == boost::system::errc::operation_not_supported)
2418 {
2419 messages::resourceInStandby(asyncResp->res);
2420 }
2421 else if (ec.value() ==
2422 boost::system::errc::device_or_resource_busy)
2423 {
2424 messages::serviceTemporarilyUnavailable(asyncResp->res,
2425 "60");
2426 }
2427 else
2428 {
2429 messages::internalError(asyncResp->res);
2430 }
2431 return;
2432 }
2433 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
2434 [](boost::system::error_code err, sdbusplus::message::message&,
2435 const std::shared_ptr<task::TaskData>& taskData) {
2436 if (!err)
2437 {
2438 taskData->messages.emplace_back(
2439 messages::taskCompletedOK(
2440 std::to_string(taskData->index)));
2441 taskData->state = "Completed";
2442 }
2443 return task::completed;
2444 },
2445 "type='signal',interface='org.freedesktop.DBus.Properties',"
2446 "member='PropertiesChanged',arg0namespace='com.intel."
2447 "crashdump'");
2448 task->startTimer(std::chrono::minutes(5));
2449 task->populateResp(asyncResp->res);
2450 task->payload.emplace(req);
2451 };
2452 crow::connections::systemBus->async_method_call(
2453 std::move(generateTelemetryLogCallback), crashdumpObject,
2454 crashdumpPath, crashdumpTelemetryInterface, "GenerateTelemetryLog");
2455 }
2456};
2457
Jason M. Billse1f26342018-07-18 12:12:00 -07002458class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002459{
2460 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002461 SendRawPECI(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002462 Node(app,
2463 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2464 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002465 {
AppaRao Puli39460282020-04-07 17:03:04 +05302466 // Note: Deviated from redfish privilege registry for GET & HEAD
2467 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002468 entityPrivileges = {
2469 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2470 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2471 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2472 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2473 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2474 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2475 }
2476
2477 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002478 void doPost(crow::Response& res, const crow::Request& req,
2479 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002480 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002481 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002482 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002483
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002484 nlohmann::json reqJson =
2485 nlohmann::json::parse(req.body, nullptr, false);
2486 if (reqJson.find("PECICommands") != reqJson.end())
2487 {
2488 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2489 {
2490 return;
2491 }
2492 uint32_t idx = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002493 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002494 {
2495 if (cmd.size() < 3)
2496 {
2497 std::string s("[");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002498 for (auto const& val : cmd)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002499 {
2500 if (val != *cmd.begin())
2501 {
2502 s += ",";
2503 }
2504 s += std::to_string(val);
2505 }
2506 s += "]";
2507 messages::actionParameterValueFormatError(
2508 res, s, "PECICommands[" + std::to_string(idx) + "]",
2509 "SendRawPeci");
2510 return;
2511 }
2512 idx++;
2513 }
2514 }
2515 else
2516 {
2517 /* This interface is deprecated */
2518 uint8_t clientAddress = 0;
2519 uint8_t readLength = 0;
2520 std::vector<uint8_t> peciCommand;
2521 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2522 "ReadLength", readLength, "PECICommand",
2523 peciCommand))
2524 {
2525 return;
2526 }
2527 peciCommands.push_back({clientAddress, 0, readLength});
2528 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2529 peciCommand.end());
2530 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002531 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002532 auto sendRawPECICallback =
2533 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002534 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002535 if (ec)
2536 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002537 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002538 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002539 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002540 return;
2541 }
2542 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2543 {"PECIResponse", resp}};
2544 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002545 // Call the SendRawPECI command with the provided data
2546 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002547 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002548 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002549 }
2550};
2551
Andrew Geisslercb92c032018-08-17 07:56:14 -07002552/**
2553 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2554 */
2555class DBusLogServiceActionsClear : public Node
2556{
2557 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002558 DBusLogServiceActionsClear(CrowApp& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002559 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002560 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002561 {
2562 entityPrivileges = {
2563 {boost::beast::http::verb::get, {{"Login"}}},
2564 {boost::beast::http::verb::head, {{"Login"}}},
2565 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2566 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2567 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2568 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2569 }
2570
2571 private:
2572 /**
2573 * Function handles POST method request.
2574 * The Clear Log actions does not require any parameter.The action deletes
2575 * all entries found in the Entries collection for this Log Service.
2576 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002577 void doPost(crow::Response& res, const crow::Request& req,
2578 const std::vector<std::string>& params) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07002579 {
2580 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2581
2582 auto asyncResp = std::make_shared<AsyncResp>(res);
2583 // Process response from Logging service.
2584 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2585 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2586 if (ec)
2587 {
2588 // TODO Handle for specific error code
2589 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2590 asyncResp->res.result(
2591 boost::beast::http::status::internal_server_error);
2592 return;
2593 }
2594
2595 asyncResp->res.result(boost::beast::http::status::no_content);
2596 };
2597
2598 // Make call to Logging service to request Clear Log
2599 crow::connections::systemBus->async_method_call(
2600 resp_handler, "xyz.openbmc_project.Logging",
2601 "/xyz/openbmc_project/logging",
2602 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2603 }
2604};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002605
2606/****************************************************
2607 * Redfish PostCode interfaces
2608 * using DBUS interface: getPostCodesTS
2609 ******************************************************/
2610class PostCodesLogService : public Node
2611{
2612 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002613 PostCodesLogService(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002614 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2615 {
2616 entityPrivileges = {
2617 {boost::beast::http::verb::get, {{"Login"}}},
2618 {boost::beast::http::verb::head, {{"Login"}}},
2619 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2620 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2621 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2622 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2623 }
2624
2625 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002626 void doGet(crow::Response& res, const crow::Request& req,
2627 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002628 {
2629 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2630
2631 asyncResp->res.jsonValue = {
2632 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2633 {"@odata.type", "#LogService.v1_1_0.LogService"},
2634 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2635 {"Name", "POST Code Log Service"},
2636 {"Description", "POST Code Log Service"},
2637 {"Id", "BIOS POST Code Log"},
2638 {"OverWritePolicy", "WrapsWhenFull"},
2639 {"Entries",
2640 {{"@odata.id",
2641 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2642 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2643 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2644 "Actions/LogService.ClearLog"}};
2645 }
2646};
2647
2648class PostCodesClear : public Node
2649{
2650 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002651 PostCodesClear(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002652 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2653 "LogService.ClearLog/")
2654 {
2655 entityPrivileges = {
2656 {boost::beast::http::verb::get, {{"Login"}}},
2657 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302658 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2659 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2660 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2661 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002662 }
2663
2664 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002665 void doPost(crow::Response& res, const crow::Request& req,
2666 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002667 {
2668 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2669
2670 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2671 // Make call to post-code service to request clear all
2672 crow::connections::systemBus->async_method_call(
2673 [asyncResp](const boost::system::error_code ec) {
2674 if (ec)
2675 {
2676 // TODO Handle for specific error code
2677 BMCWEB_LOG_ERROR
2678 << "doClearPostCodes resp_handler got error " << ec;
2679 asyncResp->res.result(
2680 boost::beast::http::status::internal_server_error);
2681 messages::internalError(asyncResp->res);
2682 return;
2683 }
2684 },
2685 "xyz.openbmc_project.State.Boot.PostCode",
2686 "/xyz/openbmc_project/State/Boot/PostCode",
2687 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2688 }
2689};
2690
2691static void fillPostCodeEntry(
2692 std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002693 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002694 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2695 const uint64_t skip = 0, const uint64_t top = 0)
2696{
2697 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002698 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08002699 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08002700
2701 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002702 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08002703
2704 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002705 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002706 {
2707 currentCodeIndex++;
2708 std::string postcodeEntryID =
2709 "B" + std::to_string(bootIndex) + "-" +
2710 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2711
2712 uint64_t usecSinceEpoch = code.first;
2713 uint64_t usTimeOffset = 0;
2714
2715 if (1 == currentCodeIndex)
2716 { // already incremented
2717 firstCodeTimeUs = code.first;
2718 }
2719 else
2720 {
2721 usTimeOffset = code.first - firstCodeTimeUs;
2722 }
2723
2724 // skip if no specific codeIndex is specified and currentCodeIndex does
2725 // not fall between top and skip
2726 if ((codeIndex == 0) &&
2727 (currentCodeIndex <= skip || currentCodeIndex > top))
2728 {
2729 continue;
2730 }
2731
2732 // skip if a sepcific codeIndex is specified and does not match the
2733 // currentIndex
2734 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2735 {
2736 // This is done for simplicity. 1st entry is needed to calculate
2737 // time offset. To improve efficiency, one can get to the entry
2738 // directly (possibly with flatmap's nth method)
2739 continue;
2740 }
2741
2742 // currentCodeIndex is within top and skip or equal to specified code
2743 // index
2744
2745 // Get the Created time from the timestamp
2746 std::string entryTimeStr;
2747 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2748 {
2749 continue;
2750 }
2751
2752 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2753 std::ostringstream hexCode;
2754 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2755 << code.second;
2756 std::ostringstream timeOffsetStr;
2757 // Set Fixed -Point Notation
2758 timeOffsetStr << std::fixed;
2759 // Set precision to 4 digits
2760 timeOffsetStr << std::setprecision(4);
2761 // Add double to stream
2762 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2763 std::vector<std::string> messageArgs = {
2764 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2765
2766 // Get MessageArgs template from message registry
2767 std::string msg;
2768 if (message != nullptr)
2769 {
2770 msg = message->message;
2771
2772 // fill in this post code value
2773 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002774 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002775 {
2776 std::string argStr = "%" + std::to_string(++i);
2777 size_t argPos = msg.find(argStr);
2778 if (argPos != std::string::npos)
2779 {
2780 msg.replace(argPos, argStr.length(), messageArg);
2781 }
2782 }
2783 }
2784
Tim Leed4342a92020-04-27 11:47:58 +08002785 // Get Severity template from message registry
2786 std::string severity;
2787 if (message != nullptr)
2788 {
2789 severity = message->severity;
2790 }
2791
ZhikuiRena3316fc2020-01-29 14:58:08 -08002792 // add to AsyncResp
2793 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002794 nlohmann::json& bmcLogEntry = logEntryArray.back();
ZhikuiRena3316fc2020-01-29 14:58:08 -08002795 bmcLogEntry = {
2796 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2797 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2798 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2799 "PostCodes/Entries/" +
2800 postcodeEntryID},
2801 {"Name", "POST Code Log Entry"},
2802 {"Id", postcodeEntryID},
2803 {"Message", std::move(msg)},
2804 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2805 {"MessageArgs", std::move(messageArgs)},
2806 {"EntryType", "Event"},
2807 {"Severity", std::move(severity)},
2808 {"Created", std::move(entryTimeStr)}};
2809 }
2810}
2811
2812static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2813 const uint16_t bootIndex,
2814 const uint64_t codeIndex)
2815{
2816 crow::connections::systemBus->async_method_call(
2817 [aResp, bootIndex, codeIndex](
2818 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002819 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002820 if (ec)
2821 {
2822 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2823 messages::internalError(aResp->res);
2824 return;
2825 }
2826
2827 // skip the empty postcode boots
2828 if (postcode.empty())
2829 {
2830 return;
2831 }
2832
2833 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2834
2835 aResp->res.jsonValue["Members@odata.count"] =
2836 aResp->res.jsonValue["Members"].size();
2837 },
2838 "xyz.openbmc_project.State.Boot.PostCode",
2839 "/xyz/openbmc_project/State/Boot/PostCode",
2840 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2841 bootIndex);
2842}
2843
2844static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2845 const uint16_t bootIndex,
2846 const uint16_t bootCount,
2847 const uint64_t entryCount, const uint64_t skip,
2848 const uint64_t top)
2849{
2850 crow::connections::systemBus->async_method_call(
2851 [aResp, bootIndex, bootCount, entryCount, skip,
2852 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002853 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002854 if (ec)
2855 {
2856 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2857 messages::internalError(aResp->res);
2858 return;
2859 }
2860
2861 uint64_t endCount = entryCount;
2862 if (!postcode.empty())
2863 {
2864 endCount = entryCount + postcode.size();
2865
2866 if ((skip < endCount) && ((top + skip) > entryCount))
2867 {
2868 uint64_t thisBootSkip =
2869 std::max(skip, entryCount) - entryCount;
2870 uint64_t thisBootTop =
2871 std::min(top + skip, endCount) - entryCount;
2872
2873 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2874 thisBootSkip, thisBootTop);
2875 }
2876 aResp->res.jsonValue["Members@odata.count"] = endCount;
2877 }
2878
2879 // continue to previous bootIndex
2880 if (bootIndex < bootCount)
2881 {
2882 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2883 bootCount, endCount, skip, top);
2884 }
2885 else
2886 {
2887 aResp->res.jsonValue["Members@odata.nextLink"] =
2888 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2889 "Entries?$skip=" +
2890 std::to_string(skip + top);
2891 }
2892 },
2893 "xyz.openbmc_project.State.Boot.PostCode",
2894 "/xyz/openbmc_project/State/Boot/PostCode",
2895 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2896 bootIndex);
2897}
2898
2899static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2900 const uint64_t skip, const uint64_t top)
2901{
2902 uint64_t entryCount = 0;
2903 crow::connections::systemBus->async_method_call(
2904 [aResp, entryCount, skip,
2905 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002906 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002907 if (ec)
2908 {
2909 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2910 messages::internalError(aResp->res);
2911 return;
2912 }
2913 auto pVal = std::get_if<uint16_t>(&bootCount);
2914 if (pVal)
2915 {
2916 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2917 }
2918 else
2919 {
2920 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2921 }
2922 },
2923 "xyz.openbmc_project.State.Boot.PostCode",
2924 "/xyz/openbmc_project/State/Boot/PostCode",
2925 "org.freedesktop.DBus.Properties", "Get",
2926 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2927}
2928
2929class PostCodesEntryCollection : public Node
2930{
2931 public:
2932 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002933 PostCodesEntryCollection(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002934 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2935 {
2936 entityPrivileges = {
2937 {boost::beast::http::verb::get, {{"Login"}}},
2938 {boost::beast::http::verb::head, {{"Login"}}},
2939 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2940 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2941 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2942 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2943 }
2944
2945 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002946 void doGet(crow::Response& res, const crow::Request& req,
2947 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002948 {
2949 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2950
2951 asyncResp->res.jsonValue["@odata.type"] =
2952 "#LogEntryCollection.LogEntryCollection";
2953 asyncResp->res.jsonValue["@odata.context"] =
2954 "/redfish/v1/"
2955 "$metadata#LogEntryCollection.LogEntryCollection";
2956 asyncResp->res.jsonValue["@odata.id"] =
2957 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2958 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2959 asyncResp->res.jsonValue["Description"] =
2960 "Collection of POST Code Log Entries";
2961 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2962 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2963
2964 uint64_t skip = 0;
2965 uint64_t top = maxEntriesPerPage; // Show max entries by default
2966 if (!getSkipParam(asyncResp->res, req, skip))
2967 {
2968 return;
2969 }
2970 if (!getTopParam(asyncResp->res, req, top))
2971 {
2972 return;
2973 }
2974 getCurrentBootNumber(asyncResp, skip, top);
2975 }
2976};
2977
2978class PostCodesEntry : public Node
2979{
2980 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002981 PostCodesEntry(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002982 Node(app,
2983 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2984 std::string())
2985 {
2986 entityPrivileges = {
2987 {boost::beast::http::verb::get, {{"Login"}}},
2988 {boost::beast::http::verb::head, {{"Login"}}},
2989 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2990 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2991 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2992 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2993 }
2994
2995 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002996 void doGet(crow::Response& res, const crow::Request& req,
2997 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002998 {
2999 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
3000 if (params.size() != 1)
3001 {
3002 messages::internalError(asyncResp->res);
3003 return;
3004 }
3005
Gunnar Mills1214b7e2020-06-04 10:11:30 -05003006 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08003007
3008 size_t bootPos = targetID.find('B');
3009 if (bootPos == std::string::npos)
3010 {
3011 // Requested ID was not found
3012 messages::resourceMissingAtURI(asyncResp->res, targetID);
3013 return;
3014 }
3015 std::string_view bootIndexStr(targetID);
3016 bootIndexStr.remove_prefix(bootPos + 1);
3017 uint16_t bootIndex = 0;
3018 uint64_t codeIndex = 0;
3019 size_t dashPos = bootIndexStr.find('-');
3020
3021 if (dashPos == std::string::npos)
3022 {
3023 return;
3024 }
3025 std::string_view codeIndexStr(bootIndexStr);
3026 bootIndexStr.remove_suffix(dashPos);
3027 codeIndexStr.remove_prefix(dashPos + 1);
3028
3029 bootIndex = static_cast<uint16_t>(
3030 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
3031 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
3032 if (bootIndex == 0 || codeIndex == 0)
3033 {
3034 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
3035 << params[0];
3036 }
3037
3038 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
3039 asyncResp->res.jsonValue["@odata.context"] =
3040 "/redfish/v1/$metadata#LogEntry.LogEntry";
3041 asyncResp->res.jsonValue["@odata.id"] =
3042 "/redfish/v1/Systems/system/LogServices/PostCodes/"
3043 "Entries";
3044 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
3045 asyncResp->res.jsonValue["Description"] =
3046 "Collection of POST Code Log Entries";
3047 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
3048 asyncResp->res.jsonValue["Members@odata.count"] = 0;
3049
3050 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
3051 }
3052};
3053
Ed Tanous1da66f72018-07-27 16:13:37 -07003054} // namespace redfish