blob: 753334e3afd5dfa682a3f0d37b5c70262cddd26f [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";
42constexpr char const* crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
43constexpr char const* crashdumpInterface = "com.intel.crashdump";
44constexpr char const* deleteAllInterface =
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070045 "xyz.openbmc_project.Collection.DeleteAll";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046constexpr char const* crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070047 "com.intel.crashdump.OnDemand";
Gunnar Mills1214b7e2020-06-04 10:11:30 -050048constexpr char const* crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070049 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070050
Jason M. Bills4851d452019-03-28 11:27:48 -070051namespace message_registries
52{
Gunnar Mills1214b7e2020-06-04 10:11:30 -050053static const Message* getMessageFromRegistry(
54 const std::string& messageKey,
Jason M. Bills4851d452019-03-28 11:27:48 -070055 const boost::beast::span<const MessageEntry> registry)
56{
57 boost::beast::span<const MessageEntry>::const_iterator messageIt =
58 std::find_if(registry.cbegin(), registry.cend(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -050059 [&messageKey](const MessageEntry& messageEntry) {
Jason M. Bills4851d452019-03-28 11:27:48 -070060 return !std::strcmp(messageEntry.first,
61 messageKey.c_str());
62 });
63 if (messageIt != registry.cend())
64 {
65 return &messageIt->second;
66 }
67
68 return nullptr;
69}
70
Gunnar Mills1214b7e2020-06-04 10:11:30 -050071static const Message* getMessage(const std::string_view& messageID)
Jason M. Bills4851d452019-03-28 11:27:48 -070072{
73 // Redfish MessageIds are in the form
74 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
75 // the right Message
76 std::vector<std::string> fields;
77 fields.reserve(4);
78 boost::split(fields, messageID, boost::is_any_of("."));
Gunnar Mills1214b7e2020-06-04 10:11:30 -050079 std::string& registryName = fields[0];
80 std::string& messageKey = fields[3];
Jason M. Bills4851d452019-03-28 11:27:48 -070081
82 // Find the right registry and check it for the MessageKey
83 if (std::string(base::header.registryPrefix) == registryName)
84 {
85 return getMessageFromRegistry(
86 messageKey, boost::beast::span<const MessageEntry>(base::registry));
87 }
88 if (std::string(openbmc::header.registryPrefix) == registryName)
89 {
90 return getMessageFromRegistry(
91 messageKey,
92 boost::beast::span<const MessageEntry>(openbmc::registry));
93 }
94 return nullptr;
95}
96} // namespace message_registries
97
James Feistf6150402019-01-08 10:36:20 -080098namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070099
Andrew Geisslercb92c032018-08-17 07:56:14 -0700100using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -0500101 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
102 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700103
104using GetManagedObjectsType = boost::container::flat_map<
105 sdbusplus::message::object_path,
106 boost::container::flat_map<std::string, GetManagedPropertyType>>;
107
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500108inline std::string translateSeverityDbusToRedfish(const std::string& s)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700109{
110 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
111 {
112 return "Critical";
113 }
114 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
115 {
116 return "Critical";
117 }
118 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
119 {
120 return "OK";
121 }
122 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
123 {
124 return "Critical";
125 }
126 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
127 {
128 return "Critical";
129 }
130 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
131 {
132 return "OK";
133 }
134 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
135 {
136 return "OK";
137 }
138 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
139 {
140 return "Warning";
141 }
142 return "";
143}
144
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500145inline void deleteSystemDumpEntry(crow::Response& res,
146 const std::string& entryID)
raviteja-bc9bb6862020-02-03 11:53:32 -0600147{
148 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
149
150 auto respHandler = [asyncResp](const boost::system::error_code ec) {
151 BMCWEB_LOG_DEBUG << "System Dump Entry doDelete callback: Done";
152 if (ec)
153 {
154 BMCWEB_LOG_ERROR
155 << "System Dump (DBus) doDelete respHandler got error " << ec;
156 asyncResp->res.result(
157 boost::beast::http::status::internal_server_error);
158 return;
159 }
raviteja-bc9bb6862020-02-03 11:53:32 -0600160 };
161 crow::connections::systemBus->async_method_call(
162 respHandler, "xyz.openbmc_project.Dump.Manager",
163 "/xyz/openbmc_project/dump/entry/" + entryID,
164 "xyz.openbmc_project.Object.Delete", "Delete");
165}
166
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500167static int getJournalMetadata(sd_journal* journal,
168 const std::string_view& field,
169 std::string_view& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700170{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500171 const char* data = nullptr;
Jason M. Bills16428a12018-11-02 12:42:29 -0700172 size_t length = 0;
173 int ret = 0;
174 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700175 ret = sd_journal_get_data(journal, field.data(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500176 reinterpret_cast<const void**>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700177 if (ret < 0)
178 {
179 return ret;
180 }
Ed Tanous39e77502019-03-04 17:35:53 -0800181 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700182 // Only use the content after the "=" character.
183 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
184 return ret;
185}
186
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500187static int getJournalMetadata(sd_journal* journal,
188 const std::string_view& field, const int& base,
189 long int& contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700190{
191 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800192 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700193 // Get the metadata from the requested field of the journal entry
194 ret = getJournalMetadata(journal, field, metadata);
195 if (ret < 0)
196 {
197 return ret;
198 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000199 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700200 return ret;
201}
202
ZhikuiRena3316fc2020-01-29 14:58:08 -0800203static bool getTimestampStr(const uint64_t usecSinceEpoch,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500204 std::string& entryTimestamp)
Jason M. Bills16428a12018-11-02 12:42:29 -0700205{
ZhikuiRena3316fc2020-01-29 14:58:08 -0800206 time_t t = static_cast<time_t>(usecSinceEpoch / 1000 / 1000);
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500207 struct tm* loctime = localtime(&t);
Jason M. Bills16428a12018-11-02 12:42:29 -0700208 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700209 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700210 {
211 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
212 }
213 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800214 std::string_view t1(entryTime);
215 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700216 if (t1.size() > 2 && t2.size() > 2)
217 {
218 t1.remove_suffix(2);
219 t2.remove_prefix(t2.size() - 2);
220 }
Ed Tanous39e77502019-03-04 17:35:53 -0800221 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700222 return true;
223}
224
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500225static bool getEntryTimestamp(sd_journal* journal, std::string& entryTimestamp)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800226{
227 int ret = 0;
228 uint64_t timestamp = 0;
229 ret = sd_journal_get_realtime_usec(journal, &timestamp);
230 if (ret < 0)
231 {
232 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
233 << strerror(-ret);
234 return false;
235 }
236 return getTimestampStr(timestamp, entryTimestamp);
237}
238
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500239static bool getSkipParam(crow::Response& res, const crow::Request& req,
240 uint64_t& skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700241{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500242 char* skipParam = req.urlParams.get("$skip");
Jason M. Bills16428a12018-11-02 12:42:29 -0700243 if (skipParam != nullptr)
244 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500245 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700246 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700247 if (*skipParam == '\0' || *ptr != '\0')
248 {
249
250 messages::queryParameterValueTypeError(res, std::string(skipParam),
251 "$skip");
252 return false;
253 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700254 }
255 return true;
256}
257
Ed Tanous271584a2019-07-09 16:24:22 -0700258static constexpr const uint64_t maxEntriesPerPage = 1000;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500259static bool getTopParam(crow::Response& res, const crow::Request& req,
260 uint64_t& top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700261{
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500262 char* topParam = req.urlParams.get("$top");
Jason M. Bills16428a12018-11-02 12:42:29 -0700263 if (topParam != nullptr)
264 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500265 char* ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700266 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700267 if (*topParam == '\0' || *ptr != '\0')
268 {
269 messages::queryParameterValueTypeError(res, std::string(topParam),
270 "$top");
271 return false;
272 }
Ed Tanous271584a2019-07-09 16:24:22 -0700273 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700274 {
275
276 messages::queryParameterOutOfRange(
277 res, std::to_string(top), "$top",
278 "1-" + std::to_string(maxEntriesPerPage));
279 return false;
280 }
281 }
282 return true;
283}
284
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500285static bool getUniqueEntryID(sd_journal* journal, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700286 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700287{
288 int ret = 0;
289 static uint64_t prevTs = 0;
290 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700291 if (firstEntry)
292 {
293 prevTs = 0;
294 }
295
Jason M. Bills16428a12018-11-02 12:42:29 -0700296 // Get the entry timestamp
297 uint64_t curTs = 0;
298 ret = sd_journal_get_realtime_usec(journal, &curTs);
299 if (ret < 0)
300 {
301 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
302 << strerror(-ret);
303 return false;
304 }
305 // If the timestamp isn't unique, increment the index
306 if (curTs == prevTs)
307 {
308 index++;
309 }
310 else
311 {
312 // Otherwise, reset it
313 index = 0;
314 }
315 // Save the timestamp
316 prevTs = curTs;
317
318 entryID = std::to_string(curTs);
319 if (index > 0)
320 {
321 entryID += "_" + std::to_string(index);
322 }
323 return true;
324}
325
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500326static bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
Jason M. Billse85d6b12019-07-29 17:01:15 -0700327 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700328{
Ed Tanous271584a2019-07-09 16:24:22 -0700329 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700330 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700331 if (firstEntry)
332 {
333 prevTs = 0;
334 }
335
Jason M. Bills95820182019-04-22 16:25:34 -0700336 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700337 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700338 std::tm timeStruct = {};
339 std::istringstream entryStream(logEntry);
340 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
341 {
342 curTs = std::mktime(&timeStruct);
343 }
344 // If the timestamp isn't unique, increment the index
345 if (curTs == prevTs)
346 {
347 index++;
348 }
349 else
350 {
351 // Otherwise, reset it
352 index = 0;
353 }
354 // Save the timestamp
355 prevTs = curTs;
356
357 entryID = std::to_string(curTs);
358 if (index > 0)
359 {
360 entryID += "_" + std::to_string(index);
361 }
362 return true;
363}
364
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500365static bool getTimestampFromID(crow::Response& res, const std::string& entryID,
366 uint64_t& timestamp, uint64_t& index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700367{
368 if (entryID.empty())
369 {
370 return false;
371 }
372 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800373 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700374
375 auto underscorePos = tsStr.find("_");
376 if (underscorePos != tsStr.npos)
377 {
378 // Timestamp has an index
379 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800380 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700381 indexStr.remove_prefix(underscorePos + 1);
382 std::size_t pos;
383 try
384 {
Ed Tanous39e77502019-03-04 17:35:53 -0800385 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700386 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500387 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700388 {
389 messages::resourceMissingAtURI(res, entryID);
390 return false;
391 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500392 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700393 {
394 messages::resourceMissingAtURI(res, entryID);
395 return false;
396 }
397 if (pos != indexStr.size())
398 {
399 messages::resourceMissingAtURI(res, entryID);
400 return false;
401 }
402 }
403 // Timestamp has no index
404 std::size_t pos;
405 try
406 {
Ed Tanous39e77502019-03-04 17:35:53 -0800407 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700408 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500409 catch (std::invalid_argument&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700410 {
411 messages::resourceMissingAtURI(res, entryID);
412 return false;
413 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500414 catch (std::out_of_range&)
Jason M. Bills16428a12018-11-02 12:42:29 -0700415 {
416 messages::resourceMissingAtURI(res, entryID);
417 return false;
418 }
419 if (pos != tsStr.size())
420 {
421 messages::resourceMissingAtURI(res, entryID);
422 return false;
423 }
424 return true;
425}
426
Jason M. Bills95820182019-04-22 16:25:34 -0700427static bool
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500428 getRedfishLogFiles(std::vector<std::filesystem::path>& redfishLogFiles)
Jason M. Bills95820182019-04-22 16:25:34 -0700429{
430 static const std::filesystem::path redfishLogDir = "/var/log";
431 static const std::string redfishLogFilename = "redfish";
432
433 // Loop through the directory looking for redfish log files
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500434 for (const std::filesystem::directory_entry& dirEnt :
Jason M. Bills95820182019-04-22 16:25:34 -0700435 std::filesystem::directory_iterator(redfishLogDir))
436 {
437 // If we find a redfish log file, save the path
438 std::string filename = dirEnt.path().filename();
439 if (boost::starts_with(filename, redfishLogFilename))
440 {
441 redfishLogFiles.emplace_back(redfishLogDir / filename);
442 }
443 }
444 // As the log files rotate, they are appended with a ".#" that is higher for
445 // the older logs. Since we don't expect more than 10 log files, we
446 // can just sort the list to get them in order from newest to oldest
447 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
448
449 return !redfishLogFiles.empty();
450}
451
Johnathan Mantey043a0532020-03-10 17:15:28 -0700452static void ParseCrashdumpParameters(
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500453 const std::vector<std::pair<std::string, VariantType>>& params,
454 std::string& filename, std::string& timestamp, std::string& logfile)
Johnathan Mantey043a0532020-03-10 17:15:28 -0700455{
456 for (auto property : params)
457 {
458 if (property.first == "Timestamp")
459 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500460 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500461 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700462 if (value != nullptr)
463 {
464 timestamp = *value;
465 }
466 }
467 else if (property.first == "Filename")
468 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500469 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500470 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700471 if (value != nullptr)
472 {
473 filename = *value;
474 }
475 }
476 else if (property.first == "Log")
477 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500478 const std::string* value =
Patrick Williams8d78b7a2020-05-13 11:24:20 -0500479 std::get_if<std::string>(&property.second);
Johnathan Mantey043a0532020-03-10 17:15:28 -0700480 if (value != nullptr)
481 {
482 logfile = *value;
483 }
484 }
485 }
486}
487
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500488constexpr char const* postCodeIface = "xyz.openbmc_project.State.Boot.PostCode";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800489class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700490{
491 public:
492 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500493 SystemLogServiceCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800494 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800495 {
496 entityPrivileges = {
497 {boost::beast::http::verb::get, {{"Login"}}},
498 {boost::beast::http::verb::head, {{"Login"}}},
499 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
500 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
501 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
502 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
503 }
504
505 private:
506 /**
507 * Functions triggers appropriate requests on DBus
508 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500509 void doGet(crow::Response& res, const crow::Request& req,
510 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800511 {
512 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800513 // Collections don't include the static data added by SubRoute because
514 // it has a duplicate entry for members
515 asyncResp->res.jsonValue["@odata.type"] =
516 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800517 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800518 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800519 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
520 asyncResp->res.jsonValue["Description"] =
521 "Collection of LogServices for this Computer System";
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500522 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800523 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800524 logServiceArray.push_back(
525 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
raviteja-bc9bb6862020-02-03 11:53:32 -0600526#ifdef BMCWEB_ENABLE_REDFISH_SYSTEMDUMP_LOG
527 logServiceArray.push_back(
528 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/System"}});
529#endif
530
Jason M. Billsd53dd412019-02-12 17:16:22 -0800531#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
532 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500533 {{"@odata.id",
534 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800535#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800536 asyncResp->res.jsonValue["Members@odata.count"] =
537 logServiceArray.size();
ZhikuiRena3316fc2020-01-29 14:58:08 -0800538
539 crow::connections::systemBus->async_method_call(
540 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500541 const std::vector<std::string>& subtreePath) {
ZhikuiRena3316fc2020-01-29 14:58:08 -0800542 if (ec)
543 {
544 BMCWEB_LOG_ERROR << ec;
545 return;
546 }
547
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500548 for (auto& pathStr : subtreePath)
ZhikuiRena3316fc2020-01-29 14:58:08 -0800549 {
550 if (pathStr.find("PostCode") != std::string::npos)
551 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500552 nlohmann::json& logServiceArray =
ZhikuiRena3316fc2020-01-29 14:58:08 -0800553 asyncResp->res.jsonValue["Members"];
554 logServiceArray.push_back(
555 {{"@odata.id", "/redfish/v1/Systems/system/"
556 "LogServices/PostCodes"}});
557 asyncResp->res.jsonValue["Members@odata.count"] =
558 logServiceArray.size();
559 return;
560 }
561 }
562 },
563 "xyz.openbmc_project.ObjectMapper",
564 "/xyz/openbmc_project/object_mapper",
565 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "/", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500566 std::array<const char*, 1>{postCodeIface});
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800567 }
568};
569
570class EventLogService : public Node
571{
572 public:
573 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500574 EventLogService(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800575 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800576 {
577 entityPrivileges = {
578 {boost::beast::http::verb::get, {{"Login"}}},
579 {boost::beast::http::verb::head, {{"Login"}}},
580 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
581 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
582 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
583 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
584 }
585
586 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500587 void doGet(crow::Response& res, const crow::Request& req,
588 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800589 {
590 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
591
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800592 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800593 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800594 asyncResp->res.jsonValue["@odata.type"] =
595 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800596 asyncResp->res.jsonValue["Name"] = "Event Log Service";
597 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
Gunnar Mills73ec8302020-04-14 16:02:42 -0500598 asyncResp->res.jsonValue["Id"] = "EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800599 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
600 asyncResp->res.jsonValue["Entries"] = {
601 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800602 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500603 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
604
605 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
606 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700607 }
608};
609
Tim Lee1f56a3a2019-10-09 10:17:57 +0800610class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700611{
612 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500613 JournalEventLogClear(CrowApp& app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700614 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
615 "LogService.ClearLog/")
616 {
617 entityPrivileges = {
618 {boost::beast::http::verb::get, {{"Login"}}},
619 {boost::beast::http::verb::head, {{"Login"}}},
620 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
621 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
622 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
623 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
624 }
625
626 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500627 void doPost(crow::Response& res, const crow::Request& req,
628 const std::vector<std::string>& params) override
Jason M. Bills489640c2019-05-17 09:56:36 -0700629 {
630 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
631
632 // Clear the EventLog by deleting the log files
633 std::vector<std::filesystem::path> redfishLogFiles;
634 if (getRedfishLogFiles(redfishLogFiles))
635 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500636 for (const std::filesystem::path& file : redfishLogFiles)
Jason M. Bills489640c2019-05-17 09:56:36 -0700637 {
638 std::error_code ec;
639 std::filesystem::remove(file, ec);
640 }
641 }
642
643 // Reload rsyslog so it knows to start new log files
644 crow::connections::systemBus->async_method_call(
645 [asyncResp](const boost::system::error_code ec) {
646 if (ec)
647 {
648 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
649 messages::internalError(asyncResp->res);
650 return;
651 }
652
653 messages::success(asyncResp->res);
654 },
655 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
656 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
657 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800658 }
659};
660
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500661static int fillEventLogEntryJson(const std::string& logEntryID,
Jason M. Bills95820182019-04-22 16:25:34 -0700662 const std::string logEntry,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500663 nlohmann::json& logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800664{
Jason M. Bills95820182019-04-22 16:25:34 -0700665 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700666 // First get the Timestamp
667 size_t space = logEntry.find_first_of(" ");
668 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700669 {
670 return 1;
671 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700672 std::string timestamp = logEntry.substr(0, space);
673 // Then get the log contents
674 size_t entryStart = logEntry.find_first_not_of(" ", space);
675 if (entryStart == std::string::npos)
676 {
677 return 1;
678 }
679 std::string_view entry(logEntry);
680 entry.remove_prefix(entryStart);
681 // Use split to separate the entry into its fields
682 std::vector<std::string> logEntryFields;
683 boost::split(logEntryFields, entry, boost::is_any_of(","),
684 boost::token_compress_on);
685 // We need at least a MessageId to be valid
686 if (logEntryFields.size() < 1)
687 {
688 return 1;
689 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500690 std::string& messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700691
Jason M. Bills4851d452019-03-28 11:27:48 -0700692 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500693 const message_registries::Message* message =
Jason M. Bills4851d452019-03-28 11:27:48 -0700694 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800695
Jason M. Bills4851d452019-03-28 11:27:48 -0700696 std::string msg;
697 std::string severity;
698 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800699 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700700 msg = message->message;
701 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800702 }
703
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700704 // Get the MessageArgs from the log if there are any
705 boost::beast::span<std::string> messageArgs;
706 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700707 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500708 std::string& messageArgsStart = logEntryFields[1];
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700709 // If the first string is empty, assume there are no MessageArgs
710 std::size_t messageArgsSize = 0;
711 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700712 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700713 messageArgsSize = logEntryFields.size() - 1;
714 }
715
716 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
717
718 // Fill the MessageArgs into the Message
719 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500720 for (const std::string& messageArg : messageArgs)
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700721 {
722 std::string argStr = "%" + std::to_string(++i);
723 size_t argPos = msg.find(argStr);
724 if (argPos != std::string::npos)
725 {
726 msg.replace(argPos, argStr.length(), messageArg);
727 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700728 }
729 }
730
Jason M. Bills95820182019-04-22 16:25:34 -0700731 // Get the Created time from the timestamp. The log timestamp is in RFC3339
732 // format which matches the Redfish format except for the fractional seconds
733 // between the '.' and the '+', so just remove them.
734 std::size_t dot = timestamp.find_first_of(".");
735 std::size_t plus = timestamp.find_first_of("+");
736 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800737 {
Jason M. Bills95820182019-04-22 16:25:34 -0700738 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800739 }
740
741 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700742 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700743 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800744 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700745 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700746 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800747 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700748 {"Id", logEntryID},
749 {"Message", std::move(msg)},
750 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800751 {"MessageArgs", std::move(messageArgs)},
752 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700753 {"Severity", std::move(severity)},
754 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800755 return 0;
756}
757
Anthony Wilson27062602019-04-22 02:10:09 -0500758class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800759{
760 public:
761 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500762 JournalEventLogEntryCollection(CrowApp& app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800763 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800764 {
765 entityPrivileges = {
766 {boost::beast::http::verb::get, {{"Login"}}},
767 {boost::beast::http::verb::head, {{"Login"}}},
768 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
769 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
770 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
771 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
772 }
773
774 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500775 void doGet(crow::Response& res, const crow::Request& req,
776 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800777 {
778 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700779 uint64_t skip = 0;
780 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800781 if (!getSkipParam(asyncResp->res, req, skip))
782 {
783 return;
784 }
785 if (!getTopParam(asyncResp->res, req, top))
786 {
787 return;
788 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800789 // Collections don't include the static data added by SubRoute because
790 // it has a duplicate entry for members
791 asyncResp->res.jsonValue["@odata.type"] =
792 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800793 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800794 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800795 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
796 asyncResp->res.jsonValue["Description"] =
797 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700798
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500799 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800800 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700801 // Go through the log files and create a unique ID for each entry
802 std::vector<std::filesystem::path> redfishLogFiles;
803 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000804 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700805 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700806
807 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700808 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
809 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800810 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700811 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700812 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800813 {
814 continue;
815 }
816
Jason M. Billse85d6b12019-07-29 17:01:15 -0700817 // Reset the unique ID on the first entry
818 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700819 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800820 {
Jason M. Bills95820182019-04-22 16:25:34 -0700821 entryCount++;
822 // Handle paging using skip (number of entries to skip from the
823 // start) and top (number of entries to display)
824 if (entryCount <= skip || entryCount > skip + top)
825 {
826 continue;
827 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800828
Jason M. Bills95820182019-04-22 16:25:34 -0700829 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700830 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700831 {
832 continue;
833 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800834
Jason M. Billse85d6b12019-07-29 17:01:15 -0700835 if (firstEntry)
836 {
837 firstEntry = false;
838 }
839
Jason M. Bills95820182019-04-22 16:25:34 -0700840 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500841 nlohmann::json& bmcLogEntry = logEntryArray.back();
Jason M. Bills95820182019-04-22 16:25:34 -0700842 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
843 {
844 messages::internalError(asyncResp->res);
845 return;
846 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800847 }
848 }
849 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
850 if (skip + top < entryCount)
851 {
852 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700853 "/redfish/v1/Systems/system/LogServices/EventLog/"
854 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800855 std::to_string(skip + top);
856 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500857 }
858};
859
Jason M. Bills897967d2019-07-29 17:05:30 -0700860class JournalEventLogEntry : public Node
861{
862 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500863 JournalEventLogEntry(CrowApp& app) :
Jason M. Bills897967d2019-07-29 17:05:30 -0700864 Node(app,
865 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
866 std::string())
867 {
868 entityPrivileges = {
869 {boost::beast::http::verb::get, {{"Login"}}},
870 {boost::beast::http::verb::head, {{"Login"}}},
871 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
872 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
873 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
874 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
875 }
876
877 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500878 void doGet(crow::Response& res, const crow::Request& req,
879 const std::vector<std::string>& params) override
Jason M. Bills897967d2019-07-29 17:05:30 -0700880 {
881 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
882 if (params.size() != 1)
883 {
884 messages::internalError(asyncResp->res);
885 return;
886 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500887 const std::string& targetID = params[0];
Jason M. Bills897967d2019-07-29 17:05:30 -0700888
889 // Go through the log files and check the unique ID for each entry to
890 // find the target entry
891 std::vector<std::filesystem::path> redfishLogFiles;
892 getRedfishLogFiles(redfishLogFiles);
893 std::string logEntry;
894
895 // Oldest logs are in the last file, so start there and loop backwards
896 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
897 it++)
898 {
899 std::ifstream logStream(*it);
900 if (!logStream.is_open())
901 {
902 continue;
903 }
904
905 // Reset the unique ID on the first entry
906 bool firstEntry = true;
907 while (std::getline(logStream, logEntry))
908 {
909 std::string idStr;
910 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
911 {
912 continue;
913 }
914
915 if (firstEntry)
916 {
917 firstEntry = false;
918 }
919
920 if (idStr == targetID)
921 {
922 if (fillEventLogEntryJson(idStr, logEntry,
923 asyncResp->res.jsonValue) != 0)
924 {
925 messages::internalError(asyncResp->res);
926 return;
927 }
928 return;
929 }
930 }
931 }
932 // Requested ID was not found
933 messages::resourceMissingAtURI(asyncResp->res, targetID);
934 }
935};
936
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500937class DBusEventLogEntryCollection : public Node
938{
939 public:
940 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500941 DBusEventLogEntryCollection(CrowApp& app) :
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500942 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
943 {
944 entityPrivileges = {
945 {boost::beast::http::verb::get, {{"Login"}}},
946 {boost::beast::http::verb::head, {{"Login"}}},
947 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
948 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
949 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
950 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
951 }
952
953 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500954 void doGet(crow::Response& res, const crow::Request& req,
955 const std::vector<std::string>& params) override
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500956 {
957 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
958
959 // Collections don't include the static data added by SubRoute because
960 // it has a duplicate entry for members
961 asyncResp->res.jsonValue["@odata.type"] =
962 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500963 asyncResp->res.jsonValue["@odata.id"] =
964 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
965 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
966 asyncResp->res.jsonValue["Description"] =
967 "Collection of System Event Log Entries";
968
Andrew Geisslercb92c032018-08-17 07:56:14 -0700969 // DBus implementation of EventLog/Entries
970 // Make call to Logging Service to find all log entry objects
971 crow::connections::systemBus->async_method_call(
972 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500973 GetManagedObjectsType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700974 if (ec)
975 {
976 // TODO Handle for specific error code
977 BMCWEB_LOG_ERROR
978 << "getLogEntriesIfaceData resp_handler got error "
979 << ec;
980 messages::internalError(asyncResp->res);
981 return;
982 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500983 nlohmann::json& entriesArray =
Andrew Geisslercb92c032018-08-17 07:56:14 -0700984 asyncResp->res.jsonValue["Members"];
985 entriesArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500986 for (auto& objectPath : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700987 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500988 for (auto& interfaceMap : objectPath.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -0700989 {
990 if (interfaceMap.first !=
991 "xyz.openbmc_project.Logging.Entry")
992 {
993 BMCWEB_LOG_DEBUG << "Bailing early on "
994 << interfaceMap.first;
995 continue;
996 }
997 entriesArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -0500998 nlohmann::json& thisEntry = entriesArray.back();
999 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001000 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001001 std::string* severity = nullptr;
1002 std::string* message = nullptr;
1003 for (auto& propertyMap : interfaceMap.second)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001004 {
1005 if (propertyMap.first == "Id")
1006 {
Patrick Williams8d78b7a2020-05-13 11:24:20 -05001007 id = std::get_if<uint32_t>(&propertyMap.second);
Andrew Geisslercb92c032018-08-17 07:56:14 -07001008 if (id == nullptr)
1009 {
1010 messages::propertyMissing(asyncResp->res,
1011 "Id");
1012 }
1013 }
1014 else if (propertyMap.first == "Timestamp")
1015 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001016 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001017 std::get_if<uint64_t>(&propertyMap.second);
1018 if (millisTimeStamp == nullptr)
1019 {
1020 messages::propertyMissing(asyncResp->res,
1021 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001022 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001023 }
1024 // Retrieve Created property with format:
1025 // yyyy-mm-ddThh:mm:ss
1026 std::chrono::milliseconds chronoTimeStamp(
1027 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -07001028 timestamp = std::chrono::duration_cast<
1029 std::chrono::duration<int>>(
1030 chronoTimeStamp)
1031 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -07001032 }
1033 else if (propertyMap.first == "Severity")
1034 {
1035 severity = std::get_if<std::string>(
1036 &propertyMap.second);
1037 if (severity == nullptr)
1038 {
1039 messages::propertyMissing(asyncResp->res,
1040 "Severity");
1041 }
1042 }
1043 else if (propertyMap.first == "Message")
1044 {
1045 message = std::get_if<std::string>(
1046 &propertyMap.second);
1047 if (message == nullptr)
1048 {
1049 messages::propertyMissing(asyncResp->res,
1050 "Message");
1051 }
1052 }
1053 }
1054 thisEntry = {
1055 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001056 {"@odata.id",
1057 "/redfish/v1/Systems/system/LogServices/EventLog/"
1058 "Entries/" +
1059 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001060 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001061 {"Id", std::to_string(*id)},
1062 {"Message", *message},
1063 {"EntryType", "Event"},
1064 {"Severity",
1065 translateSeverityDbusToRedfish(*severity)},
1066 {"Created", crow::utility::getDateTime(timestamp)}};
1067 }
1068 }
1069 std::sort(entriesArray.begin(), entriesArray.end(),
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001070 [](const nlohmann::json& left,
1071 const nlohmann::json& right) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001072 return (left["Id"] <= right["Id"]);
1073 });
1074 asyncResp->res.jsonValue["Members@odata.count"] =
1075 entriesArray.size();
1076 },
1077 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
1078 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001079 }
1080};
1081
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001082class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001083{
1084 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001085 DBusEventLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001086 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001087 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1088 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001089 {
1090 entityPrivileges = {
1091 {boost::beast::http::verb::get, {{"Login"}}},
1092 {boost::beast::http::verb::head, {{"Login"}}},
1093 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1094 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1095 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1096 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1097 }
1098
1099 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001100 void doGet(crow::Response& res, const crow::Request& req,
1101 const std::vector<std::string>& params) override
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001102 {
1103 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001104 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001105 {
1106 messages::internalError(asyncResp->res);
1107 return;
1108 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001109 const std::string& entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001110
Andrew Geisslercb92c032018-08-17 07:56:14 -07001111 // DBus implementation of EventLog/Entries
1112 // Make call to Logging Service to find all log entry objects
1113 crow::connections::systemBus->async_method_call(
1114 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001115 GetManagedPropertyType& resp) {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001116 if (ec)
1117 {
1118 BMCWEB_LOG_ERROR
1119 << "EventLogEntry (DBus) resp_handler got error " << ec;
1120 messages::internalError(asyncResp->res);
1121 return;
1122 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001123 uint32_t* id = nullptr;
Ed Tanous66664f22019-10-11 13:05:49 -07001124 std::time_t timestamp{};
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001125 std::string* severity = nullptr;
1126 std::string* message = nullptr;
1127 for (auto& propertyMap : resp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001128 {
1129 if (propertyMap.first == "Id")
1130 {
1131 id = std::get_if<uint32_t>(&propertyMap.second);
1132 if (id == nullptr)
1133 {
1134 messages::propertyMissing(asyncResp->res, "Id");
1135 }
1136 }
1137 else if (propertyMap.first == "Timestamp")
1138 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001139 const uint64_t* millisTimeStamp =
Andrew Geisslercb92c032018-08-17 07:56:14 -07001140 std::get_if<uint64_t>(&propertyMap.second);
1141 if (millisTimeStamp == nullptr)
1142 {
1143 messages::propertyMissing(asyncResp->res,
1144 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001145 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001146 }
1147 // Retrieve Created property with format:
1148 // yyyy-mm-ddThh:mm:ss
1149 std::chrono::milliseconds chronoTimeStamp(
1150 *millisTimeStamp);
1151 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001152 std::chrono::duration_cast<
1153 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001154 .count();
1155 }
1156 else if (propertyMap.first == "Severity")
1157 {
1158 severity =
1159 std::get_if<std::string>(&propertyMap.second);
1160 if (severity == nullptr)
1161 {
1162 messages::propertyMissing(asyncResp->res,
1163 "Severity");
1164 }
1165 }
1166 else if (propertyMap.first == "Message")
1167 {
1168 message = std::get_if<std::string>(&propertyMap.second);
1169 if (message == nullptr)
1170 {
1171 messages::propertyMissing(asyncResp->res,
1172 "Message");
1173 }
1174 }
1175 }
Ed Tanous271584a2019-07-09 16:24:22 -07001176 if (id == nullptr || message == nullptr || severity == nullptr)
1177 {
1178 return;
1179 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001180 asyncResp->res.jsonValue = {
1181 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001182 {"@odata.id",
1183 "/redfish/v1/Systems/system/LogServices/EventLog/"
1184 "Entries/" +
1185 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001186 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001187 {"Id", std::to_string(*id)},
1188 {"Message", *message},
1189 {"EntryType", "Event"},
1190 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001191 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001192 },
1193 "xyz.openbmc_project.Logging",
1194 "/xyz/openbmc_project/logging/entry/" + entryID,
1195 "org.freedesktop.DBus.Properties", "GetAll",
1196 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001197 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001198
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001199 void doDelete(crow::Response& res, const crow::Request& req,
1200 const std::vector<std::string>& params) override
Chicago Duan336e96c2019-07-15 14:22:08 +08001201 {
1202
1203 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1204
1205 auto asyncResp = std::make_shared<AsyncResp>(res);
1206
1207 if (params.size() != 1)
1208 {
1209 messages::internalError(asyncResp->res);
1210 return;
1211 }
1212 std::string entryID = params[0];
1213
1214 dbus::utility::escapePathForDbus(entryID);
1215
1216 // Process response from Logging service.
1217 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1218 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1219 if (ec)
1220 {
1221 // TODO Handle for specific error code
1222 BMCWEB_LOG_ERROR
1223 << "EventLogEntry (DBus) doDelete respHandler got error "
1224 << ec;
1225 asyncResp->res.result(
1226 boost::beast::http::status::internal_server_error);
1227 return;
1228 }
1229
1230 asyncResp->res.result(boost::beast::http::status::ok);
1231 };
1232
1233 // Make call to Logging service to request Delete Log
1234 crow::connections::systemBus->async_method_call(
1235 respHandler, "xyz.openbmc_project.Logging",
1236 "/xyz/openbmc_project/logging/entry/" + entryID,
1237 "xyz.openbmc_project.Object.Delete", "Delete");
1238 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001239};
1240
1241class BMCLogServiceCollection : public Node
1242{
1243 public:
1244 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001245 BMCLogServiceCollection(CrowApp& app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001246 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001247 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001248 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001249 {boost::beast::http::verb::get, {{"Login"}}},
1250 {boost::beast::http::verb::head, {{"Login"}}},
1251 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1252 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1253 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1254 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001255 }
1256
1257 private:
1258 /**
1259 * Functions triggers appropriate requests on DBus
1260 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001261 void doGet(crow::Response& res, const crow::Request& req,
1262 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001263 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001264 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001265 // Collections don't include the static data added by SubRoute because
1266 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001267 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001268 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001269 asyncResp->res.jsonValue["@odata.id"] =
1270 "/redfish/v1/Managers/bmc/LogServices";
1271 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1272 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001273 "Collection of LogServices for this Manager";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001274 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001275 logServiceArray = nlohmann::json::array();
1276#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1277 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001278 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001279#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001280 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001281 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001282 }
1283};
1284
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001285class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001286{
1287 public:
1288 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001289 BMCJournalLogService(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001290 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001291 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001292 entityPrivileges = {
1293 {boost::beast::http::verb::get, {{"Login"}}},
1294 {boost::beast::http::verb::head, {{"Login"}}},
1295 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1296 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1297 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1298 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1299 }
1300
1301 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001302 void doGet(crow::Response& res, const crow::Request& req,
1303 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001304 {
1305 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001306 asyncResp->res.jsonValue["@odata.type"] =
1307 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001308 asyncResp->res.jsonValue["@odata.id"] =
1309 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001310 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1311 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1312 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001313 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001314 asyncResp->res.jsonValue["Entries"] = {
1315 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001316 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001317 }
1318};
1319
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001320static int fillBMCJournalLogEntryJson(const std::string& bmcJournalLogEntryID,
1321 sd_journal* journal,
1322 nlohmann::json& bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001323{
1324 // Get the Log Entry contents
1325 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001326
Ed Tanous39e77502019-03-04 17:35:53 -08001327 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001328 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001329 if (ret < 0)
1330 {
1331 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1332 return 1;
1333 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001334
1335 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001336 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001337 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001338 if (ret < 0)
1339 {
1340 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001341 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001342
1343 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001344 std::string entryTimeStr;
1345 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001346 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001347 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001348 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001349
1350 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001351 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001352 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001353 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1354 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001355 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001356 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001357 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001358 {"EntryType", "Oem"},
1359 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001360 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001361 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001362 {"Created", std::move(entryTimeStr)}};
1363 return 0;
1364}
1365
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001366class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001367{
1368 public:
1369 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001370 BMCJournalLogEntryCollection(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001371 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001372 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001373 entityPrivileges = {
1374 {boost::beast::http::verb::get, {{"Login"}}},
1375 {boost::beast::http::verb::head, {{"Login"}}},
1376 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1377 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1378 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1379 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1380 }
1381
1382 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001383 void doGet(crow::Response& res, const crow::Request& req,
1384 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001385 {
1386 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001387 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001388 uint64_t skip = 0;
1389 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001390 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001391 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001392 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001393 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001394 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001395 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001396 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001397 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001398 // Collections don't include the static data added by SubRoute because
1399 // it has a duplicate entry for members
1400 asyncResp->res.jsonValue["@odata.type"] =
1401 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001402 asyncResp->res.jsonValue["@odata.id"] =
1403 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001404 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001405 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001406 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1407 asyncResp->res.jsonValue["Description"] =
1408 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001409 asyncResp->res.jsonValue["@odata.id"] =
1410 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001411 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07001412 logEntryArray = nlohmann::json::array();
1413
1414 // Go through the journal and use the timestamp to create a unique ID
1415 // for each entry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001416 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001417 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1418 if (ret < 0)
1419 {
1420 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001421 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001422 return;
1423 }
1424 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1425 journalTmp, sd_journal_close);
1426 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001427 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001428 // Reset the unique ID on the first entry
1429 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001430 SD_JOURNAL_FOREACH(journal.get())
1431 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001432 entryCount++;
1433 // Handle paging using skip (number of entries to skip from the
1434 // start) and top (number of entries to display)
1435 if (entryCount <= skip || entryCount > skip + top)
1436 {
1437 continue;
1438 }
1439
Jason M. Bills16428a12018-11-02 12:42:29 -07001440 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001441 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001442 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001443 continue;
1444 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001445
Jason M. Billse85d6b12019-07-29 17:01:15 -07001446 if (firstEntry)
1447 {
1448 firstEntry = false;
1449 }
1450
Jason M. Billse1f26342018-07-18 12:12:00 -07001451 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001452 nlohmann::json& bmcJournalLogEntry = logEntryArray.back();
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001453 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1454 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001455 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001456 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001457 return;
1458 }
1459 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001460 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1461 if (skip + top < entryCount)
1462 {
1463 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001464 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001465 std::to_string(skip + top);
1466 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001467 }
1468};
1469
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001470class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001471{
1472 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001473 BMCJournalLogEntry(CrowApp& app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001474 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001475 std::string())
1476 {
1477 entityPrivileges = {
1478 {boost::beast::http::verb::get, {{"Login"}}},
1479 {boost::beast::http::verb::head, {{"Login"}}},
1480 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1481 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1482 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1483 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1484 }
1485
1486 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001487 void doGet(crow::Response& res, const crow::Request& req,
1488 const std::vector<std::string>& params) override
Jason M. Billse1f26342018-07-18 12:12:00 -07001489 {
1490 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1491 if (params.size() != 1)
1492 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001493 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001494 return;
1495 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001496 const std::string& entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001497 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001498 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001499 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001500 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001501 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001502 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001503 }
1504
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001505 sd_journal* journalTmp = nullptr;
Jason M. Billse1f26342018-07-18 12:12:00 -07001506 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1507 if (ret < 0)
1508 {
1509 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001510 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001511 return;
1512 }
1513 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1514 journalTmp, sd_journal_close);
1515 journalTmp = nullptr;
1516 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001517 // tracking the unique ID
1518 std::string idStr;
1519 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001520 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Manojkiran Eda2056b6d2020-05-28 08:57:36 +05301521 if (ret < 0)
1522 {
1523 BMCWEB_LOG_ERROR << "failed to seek to an entry in journal"
1524 << strerror(-ret);
1525 messages::internalError(asyncResp->res);
1526 return;
1527 }
Ed Tanous271584a2019-07-09 16:24:22 -07001528 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001529 {
1530 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001531 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1532 {
1533 messages::internalError(asyncResp->res);
1534 return;
1535 }
1536 if (firstEntry)
1537 {
1538 firstEntry = false;
1539 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001540 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001541 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001542 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001543 {
1544 messages::resourceMissingAtURI(asyncResp->res, entryID);
1545 return;
1546 }
1547
1548 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1549 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001550 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001551 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001552 return;
1553 }
1554 }
1555};
1556
raviteja-bc9bb6862020-02-03 11:53:32 -06001557class SystemDumpService : public Node
1558{
1559 public:
1560 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001561 SystemDumpService(CrowApp& app) :
raviteja-bc9bb6862020-02-03 11:53:32 -06001562 Node(app, "/redfish/v1/Systems/system/LogServices/System/")
1563 {
1564 entityPrivileges = {
1565 {boost::beast::http::verb::get, {{"Login"}}},
1566 {boost::beast::http::verb::head, {{"Login"}}},
1567 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1568 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1569 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1570 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1571 }
1572
1573 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001574 void doGet(crow::Response& res, const crow::Request& req,
1575 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001576 {
1577 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1578
1579 asyncResp->res.jsonValue["@odata.id"] =
1580 "/redfish/v1/Systems/system/LogServices/System";
1581 asyncResp->res.jsonValue["@odata.type"] =
1582 "#LogService.v1_1_0.LogService";
1583 asyncResp->res.jsonValue["Name"] = "Dump Log Service";
1584 asyncResp->res.jsonValue["Description"] = "System Dump Log Service";
1585 asyncResp->res.jsonValue["Id"] = "System";
1586 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1587 asyncResp->res.jsonValue["LogEntryTypes"] = "Dump";
1588 asyncResp->res.jsonValue["Oem"]["DumpType"] = "System";
1589
1590 asyncResp->res.jsonValue["Entries"] = {
1591 {"@odata.id",
1592 "/redfish/v1/Systems/system/LogServices/System/Entries"}};
1593 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1594 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1595 "Actions/LogService.ClearLog"}};
1596 asyncResp->res.jsonValue["Actions"]["#LogService.CreateLog"] = {
1597 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1598 "Actions/LogService.CreateLog"}};
1599 }
1600};
1601
1602class SystemDumpEntryCollection : public Node
1603{
1604 public:
1605 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001606 SystemDumpEntryCollection(CrowApp& app) :
raviteja-bc9bb6862020-02-03 11:53:32 -06001607 Node(app, "/redfish/v1/Systems/system/LogServices/System/Entries/")
1608 {
1609 entityPrivileges = {
1610 {boost::beast::http::verb::get, {{"Login"}}},
1611 {boost::beast::http::verb::head, {{"Login"}}},
1612 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1613 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1614 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1615 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1616 }
1617
1618 private:
1619 /**
1620 * Functions triggers appropriate requests on DBus
1621 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001622 void doGet(crow::Response& res, const crow::Request& req,
1623 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001624 {
1625 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1626
1627 asyncResp->res.jsonValue["@odata.type"] =
1628 "#LogEntryCollection.LogEntryCollection";
1629 asyncResp->res.jsonValue["@odata.id"] =
1630 "/redfish/v1/Systems/system/LogServices/System/Entries";
1631 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
1632 asyncResp->res.jsonValue["Description"] =
1633 "Collection of System Dump Entries";
1634
1635 crow::connections::systemBus->async_method_call(
1636 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001637 const crow::openbmc_mapper::GetSubTreeType& resp) {
raviteja-bc9bb6862020-02-03 11:53:32 -06001638 if (ec)
1639 {
1640 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1641 messages::internalError(asyncResp->res);
1642 return;
1643 }
1644
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001645 nlohmann::json& logArray = asyncResp->res.jsonValue["Members"];
raviteja-bc9bb6862020-02-03 11:53:32 -06001646 logArray = nlohmann::json::array();
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001647 for (auto& object : resp)
raviteja-bc9bb6862020-02-03 11:53:32 -06001648 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001649 const std::string& path =
1650 static_cast<const std::string&>(object.first);
raviteja-bc9bb6862020-02-03 11:53:32 -06001651 std::size_t lastPos = path.rfind("/");
1652 if (lastPos == std::string::npos)
1653 {
1654 continue;
1655 }
1656 std::string logID = path.substr(lastPos + 1);
1657 logArray.push_back(
1658 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1659 "System/Entries/" +
1660 logID}});
1661 }
1662 asyncResp->res.jsonValue["Members@odata.count"] =
1663 logArray.size();
1664 },
1665 "xyz.openbmc_project.ObjectMapper",
1666 "/xyz/openbmc_project/object_mapper",
1667 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1668 "/xyz/openbmc_project/dump", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001669 std::array<const char*, 1>{
raviteja-bc9bb6862020-02-03 11:53:32 -06001670 "xyz.openbmc_project.Dump.Entry.System"});
1671 }
1672};
1673
1674class SystemDumpEntry : public Node
1675{
1676 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001677 SystemDumpEntry(CrowApp& app) :
raviteja-bc9bb6862020-02-03 11:53:32 -06001678 Node(app,
1679 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/",
1680 std::string())
1681 {
1682 entityPrivileges = {
1683 {boost::beast::http::verb::get, {{"Login"}}},
1684 {boost::beast::http::verb::head, {{"Login"}}},
1685 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1686 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1687 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1688 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1689 }
1690
1691 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001692 void doGet(crow::Response& res, const crow::Request& req,
1693 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001694 {
1695 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1696 if (params.size() != 1)
1697 {
1698 messages::internalError(asyncResp->res);
1699 return;
1700 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001701 const std::string& entryID = params[0];
raviteja-bc9bb6862020-02-03 11:53:32 -06001702 crow::connections::systemBus->async_method_call(
1703 [asyncResp, entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001704 GetManagedObjectsType& resp) {
raviteja-bc9bb6862020-02-03 11:53:32 -06001705 if (ec)
1706 {
1707 BMCWEB_LOG_ERROR
1708 << "SystemDumpEntry resp_handler got error " << ec;
1709 messages::internalError(asyncResp->res);
1710 return;
1711 }
1712
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001713 for (auto& objectPath : resp)
raviteja-bc9bb6862020-02-03 11:53:32 -06001714 {
1715 if (objectPath.first.str.find(
1716 "/xyz/openbmc_project/dump/entry/" + entryID) ==
1717 std::string::npos)
1718 {
1719 continue;
1720 }
1721
1722 bool foundSystemDumpEntry = false;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001723 for (auto& interfaceMap : objectPath.second)
raviteja-bc9bb6862020-02-03 11:53:32 -06001724 {
1725 if (interfaceMap.first ==
1726 "xyz.openbmc_project.Dump.Entry.System")
1727 {
1728 foundSystemDumpEntry = true;
1729 break;
1730 }
1731 }
1732 if (foundSystemDumpEntry == false)
1733 {
1734 BMCWEB_LOG_DEBUG << "Can't find System Dump Entry";
1735 messages::internalError(asyncResp->res);
1736 return;
1737 }
1738
1739 std::string timestamp{};
1740 uint64_t size = 0;
1741
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001742 for (auto& interfaceMap : objectPath.second)
raviteja-bc9bb6862020-02-03 11:53:32 -06001743 {
1744 if (interfaceMap.first ==
1745 "xyz.openbmc_project.Dump.Entry")
1746 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001747 for (auto& propertyMap : interfaceMap.second)
raviteja-bc9bb6862020-02-03 11:53:32 -06001748 {
1749 if (propertyMap.first == "Size")
1750 {
1751 auto sizePtr = std::get_if<uint64_t>(
1752 &propertyMap.second);
1753 if (sizePtr == nullptr)
1754 {
1755 messages::propertyMissing(
1756 asyncResp->res, "Size");
1757 break;
1758 }
1759 size = *sizePtr;
1760 break;
1761 }
1762 }
1763 }
1764 else if (interfaceMap.first ==
1765 "xyz.openbmc_project.Time.EpochTime")
1766 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001767 for (auto& propertyMap : interfaceMap.second)
raviteja-bc9bb6862020-02-03 11:53:32 -06001768 {
1769 if (propertyMap.first == "Elapsed")
1770 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001771 const uint64_t* usecsTimeStamp =
raviteja-bc9bb6862020-02-03 11:53:32 -06001772 std::get_if<uint64_t>(
1773 &propertyMap.second);
1774 if (usecsTimeStamp == nullptr)
1775 {
1776 messages::propertyMissing(
1777 asyncResp->res, "Elapsed");
1778 break;
1779 }
1780 getTimestampStr(*usecsTimeStamp, timestamp);
1781 break;
1782 }
1783 }
1784 }
1785 }
1786 asyncResp->res.jsonValue = {
1787 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1788 {"@odata.id",
1789 "/redfish/v1/Systems/system/LogServices/System/"
1790 "Entries/" +
1791 entryID},
1792 {"Name", "System Dump Entry"},
1793 {"Id", entryID},
1794 {"SizeInB", size},
1795 {"EntryType", "Dump"},
1796 {"EntryCode", "User generated dump"},
1797 {"Created", timestamp}};
1798
1799 asyncResp->res
1800 .jsonValue["Actions"]["#LogEntry.DownloadLog"] = {
1801 {"target",
1802 "/redfish/v1/Systems/system/LogServices/System/"
1803 "Entries/" +
1804 entryID + "/Actions/LogEntry.DownloadLog"}};
1805 }
1806 },
1807 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
1808 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1809 }
1810
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001811 void doDelete(crow::Response& res, const crow::Request& req,
1812 const std::vector<std::string>& params) override
raviteja-bc9bb6862020-02-03 11:53:32 -06001813 {
1814 BMCWEB_LOG_DEBUG << "Do delete single dump entry";
1815
1816 auto asyncResp = std::make_shared<AsyncResp>(res);
1817
1818 if (params.size() != 1)
1819 {
1820 messages::internalError(asyncResp->res);
1821 return;
1822 }
1823 std::string entryID = params[0];
1824
1825 crow::connections::systemBus->async_method_call(
1826 [asyncResp,
1827 entryID](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001828 const crow::openbmc_mapper::GetSubTreeType& resp) {
raviteja-bc9bb6862020-02-03 11:53:32 -06001829 if (ec)
1830 {
1831 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1832 messages::internalError(asyncResp->res);
1833 return;
1834 }
1835
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001836 for (auto& object : resp)
raviteja-bc9bb6862020-02-03 11:53:32 -06001837 {
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001838 const std::string& path =
1839 static_cast<const std::string&>(object.first);
raviteja-bc9bb6862020-02-03 11:53:32 -06001840
1841 std::size_t pos = path.rfind(
1842 "/xyz/openbmc_project/dump/entry/" + entryID);
1843 if (pos != std::string::npos)
1844 {
1845 deleteSystemDumpEntry(asyncResp->res, entryID);
1846 return;
1847 }
1848 }
1849 },
1850 "xyz.openbmc_project.ObjectMapper",
1851 "/xyz/openbmc_project/object_mapper",
1852 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1853 "/xyz/openbmc_project/dump", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001854 std::array<const char*, 1>{
raviteja-bc9bb6862020-02-03 11:53:32 -06001855 "xyz.openbmc_project.Dump.Entry.System"});
1856 }
1857};
1858
raviteja-b06578432020-02-03 12:50:42 -06001859class SystemDumpEntryDownload : public Node
1860{
1861 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001862 SystemDumpEntryDownload(CrowApp& app) :
raviteja-b06578432020-02-03 12:50:42 -06001863 Node(app,
1864 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/"
1865 "Actions/"
1866 "LogEntry.DownloadLog/",
1867 std::string())
1868 {
1869 entityPrivileges = {
1870 {boost::beast::http::verb::get, {{"Login"}}},
1871 {boost::beast::http::verb::head, {{"Login"}}},
1872 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1873 }
1874
1875 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001876 void doPost(crow::Response& res, const crow::Request& req,
1877 const std::vector<std::string>& params) override
raviteja-b06578432020-02-03 12:50:42 -06001878 {
1879 if (params.size() != 1)
1880 {
1881 messages::internalError(res);
1882 return;
1883 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001884 const std::string& entryID = params[0];
raviteja-b06578432020-02-03 12:50:42 -06001885 crow::obmc_dump::handleDumpOffloadUrl(req, res, entryID);
1886 }
1887};
1888
raviteja-b013487e2020-03-03 03:20:48 -06001889class SystemDumpClear : public Node
1890{
1891 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001892 SystemDumpClear(CrowApp& app) :
raviteja-b013487e2020-03-03 03:20:48 -06001893 Node(app, "/redfish/v1/Systems/system/LogServices/System/"
1894 "Actions/"
1895 "LogService.ClearLog/")
1896 {
1897 entityPrivileges = {
1898 {boost::beast::http::verb::get, {{"Login"}}},
1899 {boost::beast::http::verb::head, {{"Login"}}},
1900 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1901 }
1902
1903 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001904 void doPost(crow::Response& res, const crow::Request& req,
1905 const std::vector<std::string>& params) override
raviteja-b013487e2020-03-03 03:20:48 -06001906 {
1907
1908 auto asyncResp = std::make_shared<AsyncResp>(res);
1909 crow::connections::systemBus->async_method_call(
1910 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001911 const std::vector<std::string>& dumpList) {
raviteja-b013487e2020-03-03 03:20:48 -06001912 if (ec)
1913 {
1914 messages::internalError(asyncResp->res);
1915 return;
1916 }
1917
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001918 for (const std::string& objectPath : dumpList)
raviteja-b013487e2020-03-03 03:20:48 -06001919 {
1920 std::size_t pos = objectPath.rfind("/");
1921 if (pos != std::string::npos)
1922 {
1923 std::string logID = objectPath.substr(pos + 1);
1924 deleteSystemDumpEntry(asyncResp->res, logID);
1925 }
1926 }
1927 },
1928 "xyz.openbmc_project.ObjectMapper",
1929 "/xyz/openbmc_project/object_mapper",
1930 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths",
1931 "/xyz/openbmc_project/dump", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001932 std::array<const char*, 1>{
raviteja-b013487e2020-03-03 03:20:48 -06001933 "xyz.openbmc_project.Dump.Entry.System"});
1934 }
1935};
1936
Jason M. Bills424c4172019-03-21 13:50:33 -07001937class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001938{
1939 public:
1940 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001941 CrashdumpService(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001942 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001943 {
AppaRao Puli39460282020-04-07 17:03:04 +05301944 // Note: Deviated from redfish privilege registry for GET & HEAD
1945 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001946 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301947 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1948 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001949 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1950 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1951 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1952 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001953 }
1954
1955 private:
1956 /**
1957 * Functions triggers appropriate requests on DBus
1958 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001959 void doGet(crow::Response& res, const crow::Request& req,
1960 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07001961 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001962 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001963 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001964 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001965 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001966 asyncResp->res.jsonValue["@odata.type"] =
1967 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001968 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1969 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1970 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001971 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1972 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001973 asyncResp->res.jsonValue["Entries"] = {
1974 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001975 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001976 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001977 {"#LogService.ClearLog",
1978 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1979 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001980 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001981 {{"#Crashdump.OnDemand",
1982 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1983 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001984
1985#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001986 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001987 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001988 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1989 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001990#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001991 }
1992};
1993
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001994class CrashdumpClear : public Node
1995{
1996 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05001997 CrashdumpClear(CrowApp& app) :
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001998 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1999 "LogService.ClearLog/")
2000 {
AppaRao Puli39460282020-04-07 17:03:04 +05302001 // Note: Deviated from redfish privilege registry for GET & HEAD
2002 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002003 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302004 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2005 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002006 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2007 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2008 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2009 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2010 }
2011
2012 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002013 void doPost(crow::Response& res, const crow::Request& req,
2014 const std::vector<std::string>& params) override
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002015 {
2016 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2017
2018 crow::connections::systemBus->async_method_call(
2019 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002020 const std::string& resp) {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002021 if (ec)
2022 {
2023 messages::internalError(asyncResp->res);
2024 return;
2025 }
2026 messages::success(asyncResp->res);
2027 },
2028 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
2029 }
2030};
2031
Jason M. Billse855dd22019-10-08 11:37:48 -07002032static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002033 const std::string& logID,
2034 nlohmann::json& logEntryJson)
Jason M. Billse855dd22019-10-08 11:37:48 -07002035{
Johnathan Mantey043a0532020-03-10 17:15:28 -07002036 auto getStoredLogCallback =
2037 [asyncResp, logID, &logEntryJson](
2038 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002039 const std::vector<std::pair<std::string, VariantType>>& params) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002040 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002041 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002042 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
2043 if (ec.value() ==
2044 boost::system::linux_error::bad_request_descriptor)
2045 {
2046 messages::resourceNotFound(asyncResp->res, "LogEntry",
2047 logID);
2048 }
2049 else
2050 {
2051 messages::internalError(asyncResp->res);
2052 }
2053 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08002054 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002055
Johnathan Mantey043a0532020-03-10 17:15:28 -07002056 std::string timestamp{};
2057 std::string filename{};
2058 std::string logfile{};
2059 ParseCrashdumpParameters(params, filename, timestamp, logfile);
2060
2061 if (filename.empty() || timestamp.empty())
2062 {
2063 messages::resourceMissingAtURI(asyncResp->res, logID);
2064 return;
2065 }
2066
2067 std::string crashdumpURI =
2068 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
2069 logID + "/" + filename;
2070 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2071 {"@odata.id", "/redfish/v1/Systems/system/"
2072 "LogServices/Crashdump/Entries/" +
2073 logID},
2074 {"Name", "CPU Crashdump"},
2075 {"Id", logID},
2076 {"EntryType", "Oem"},
2077 {"OemRecordFormat", "Crashdump URI"},
2078 {"Message", std::move(crashdumpURI)},
2079 {"Created", std::move(timestamp)}};
2080 };
Jason M. Billse855dd22019-10-08 11:37:48 -07002081 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002082 std::move(getStoredLogCallback), crashdumpObject,
2083 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002084 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002085}
2086
Jason M. Bills424c4172019-03-21 13:50:33 -07002087class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002088{
2089 public:
2090 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002091 CrashdumpEntryCollection(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002092 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002093 {
AppaRao Puli39460282020-04-07 17:03:04 +05302094 // Note: Deviated from redfish privilege registry for GET & HEAD
2095 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002096 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302097 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2098 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002099 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2100 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2101 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2102 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002103 }
2104
2105 private:
2106 /**
2107 * Functions triggers appropriate requests on DBus
2108 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002109 void doGet(crow::Response& res, const crow::Request& req,
2110 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002111 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002112 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002113 // Collections don't include the static data added by SubRoute because
2114 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002115 auto getLogEntriesCallback = [asyncResp](
2116 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002117 const std::vector<std::string>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002118 if (ec)
2119 {
2120 if (ec.value() !=
2121 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002122 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002123 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2124 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002125 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002126 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002127 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002128 }
2129 asyncResp->res.jsonValue["@odata.type"] =
2130 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002131 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002132 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002133 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002134 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002135 "Collection of Crashdump Entries";
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002136 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
Jason M. Billse1f26342018-07-18 12:12:00 -07002137 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002138 std::vector<std::string> logIDs;
2139 // Get the list of log entries and build up an empty array big
2140 // enough to hold them
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002141 for (const std::string& objpath : resp)
Jason M. Billse1f26342018-07-18 12:12:00 -07002142 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002143 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002144 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002145 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002146 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002147 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002148 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002149 logIDs.emplace_back(objpath.substr(lastPos + 1));
2150
2151 // Add a space for the log entry to the array
2152 logEntryArray.push_back({});
2153 }
2154 // Now go through and set up async calls to fill in the entries
2155 size_t index = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002156 for (const std::string& logID : logIDs)
Jason M. Billse855dd22019-10-08 11:37:48 -07002157 {
2158 // Add the log entry to the array
2159 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002160 }
2161 asyncResp->res.jsonValue["Members@odata.count"] =
2162 logEntryArray.size();
2163 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002164 crow::connections::systemBus->async_method_call(
2165 std::move(getLogEntriesCallback),
2166 "xyz.openbmc_project.ObjectMapper",
2167 "/xyz/openbmc_project/object_mapper",
2168 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002169 std::array<const char*, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002170 }
2171};
2172
Jason M. Bills424c4172019-03-21 13:50:33 -07002173class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002174{
2175 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002176 CrashdumpEntry(CrowApp& app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002177 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002178 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002179 std::string())
2180 {
AppaRao Puli39460282020-04-07 17:03:04 +05302181 // Note: Deviated from redfish privilege registry for GET & HEAD
2182 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002183 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302184 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2185 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002186 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2187 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2188 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2189 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002190 }
2191
2192 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002193 void doGet(crow::Response& res, const crow::Request& req,
2194 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002195 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002196 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002197 if (params.size() != 1)
2198 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002199 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002200 return;
2201 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002202 const std::string& logID = params[0];
Jason M. Billse855dd22019-10-08 11:37:48 -07002203 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2204 }
2205};
2206
2207class CrashdumpFile : public Node
2208{
2209 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002210 CrashdumpFile(CrowApp& app) :
Jason M. Billse855dd22019-10-08 11:37:48 -07002211 Node(app,
2212 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2213 "<str>/",
2214 std::string(), std::string())
2215 {
AppaRao Puli39460282020-04-07 17:03:04 +05302216 // Note: Deviated from redfish privilege registry for GET & HEAD
2217 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002218 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302219 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2220 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002221 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2222 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2223 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2224 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2225 }
2226
2227 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002228 void doGet(crow::Response& res, const crow::Request& req,
2229 const std::vector<std::string>& params) override
Jason M. Billse855dd22019-10-08 11:37:48 -07002230 {
2231 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2232 if (params.size() != 2)
2233 {
2234 messages::internalError(asyncResp->res);
2235 return;
2236 }
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002237 const std::string& logID = params[0];
2238 const std::string& fileName = params[1];
Jason M. Billse855dd22019-10-08 11:37:48 -07002239
Johnathan Mantey043a0532020-03-10 17:15:28 -07002240 auto getStoredLogCallback =
2241 [asyncResp, logID, fileName](
2242 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002243 const std::vector<std::pair<std::string, VariantType>>& resp) {
Johnathan Mantey043a0532020-03-10 17:15:28 -07002244 if (ec)
2245 {
2246 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2247 << ec.message();
2248 messages::internalError(asyncResp->res);
2249 return;
2250 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002251
Johnathan Mantey043a0532020-03-10 17:15:28 -07002252 std::string dbusFilename{};
2253 std::string dbusTimestamp{};
2254 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002255
Johnathan Mantey043a0532020-03-10 17:15:28 -07002256 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2257 dbusFilepath);
2258
2259 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2260 dbusFilepath.empty())
2261 {
2262 messages::resourceMissingAtURI(asyncResp->res, fileName);
2263 return;
2264 }
2265
2266 // Verify the file name parameter is correct
2267 if (fileName != dbusFilename)
2268 {
2269 messages::resourceMissingAtURI(asyncResp->res, fileName);
2270 return;
2271 }
2272
2273 if (!std::filesystem::exists(dbusFilepath))
2274 {
2275 messages::resourceMissingAtURI(asyncResp->res, fileName);
2276 return;
2277 }
2278 std::ifstream ifs(dbusFilepath, std::ios::in |
2279 std::ios::binary |
2280 std::ios::ate);
2281 std::ifstream::pos_type fileSize = ifs.tellg();
2282 if (fileSize < 0)
2283 {
2284 messages::generalError(asyncResp->res);
2285 return;
2286 }
2287 ifs.seekg(0, std::ios::beg);
2288
2289 auto crashData = std::make_unique<char[]>(
2290 static_cast<unsigned int>(fileSize));
2291
2292 ifs.read(crashData.get(), static_cast<int>(fileSize));
2293
2294 // The cast to std::string is intentional in order to use the
2295 // assign() that applies move mechanics
2296 asyncResp->res.body().assign(
2297 static_cast<std::string>(crashData.get()));
2298
2299 // Configure this to be a file download when accessed from
2300 // a browser
2301 asyncResp->res.addHeader("Content-Disposition", "attachment");
2302 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002303 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002304 std::move(getStoredLogCallback), crashdumpObject,
2305 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002306 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002307 }
2308};
2309
Jason M. Bills424c4172019-03-21 13:50:33 -07002310class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002311{
2312 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002313 OnDemandCrashdump(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002314 Node(app,
2315 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2316 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002317 {
AppaRao Puli39460282020-04-07 17:03:04 +05302318 // Note: Deviated from redfish privilege registry for GET & HEAD
2319 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002320 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302321 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2322 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2323 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2324 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2325 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2326 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002327 }
2328
2329 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002330 void doPost(crow::Response& res, const crow::Request& req,
2331 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002332 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002333 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002334
James Feistfe306722020-03-12 16:32:08 -07002335 auto generateonDemandLogCallback = [asyncResp,
2336 req](const boost::system::error_code
2337 ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002338 const std::string& resp) {
James Feist46229572020-02-19 15:11:58 -08002339 if (ec)
2340 {
2341 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002342 {
James Feist46229572020-02-19 15:11:58 -08002343 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002344 }
James Feist46229572020-02-19 15:11:58 -08002345 else if (ec.value() ==
2346 boost::system::errc::device_or_resource_busy)
2347 {
2348 messages::serviceTemporarilyUnavailable(asyncResp->res,
2349 "60");
2350 }
2351 else
2352 {
2353 messages::internalError(asyncResp->res);
2354 }
2355 return;
2356 }
2357 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002358 [](boost::system::error_code err, sdbusplus::message::message&,
2359 const std::shared_ptr<task::TaskData>& taskData) {
James Feist66afe4f2020-02-24 13:09:58 -08002360 if (!err)
2361 {
James Feiste5d50062020-05-11 17:29:00 -07002362 taskData->messages.emplace_back(
2363 messages::taskCompletedOK(
2364 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002365 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002366 }
James Feist32898ce2020-03-10 16:16:52 -07002367 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002368 },
James Feist46229572020-02-19 15:11:58 -08002369 "type='signal',interface='org.freedesktop.DBus.Properties',"
2370 "member='PropertiesChanged',arg0namespace='com.intel."
2371 "crashdump'");
2372 task->startTimer(std::chrono::minutes(5));
2373 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002374 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002375 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002376 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002377 std::move(generateonDemandLogCallback), crashdumpObject,
2378 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002379 }
2380};
2381
Jason M. Billse1f26342018-07-18 12:12:00 -07002382class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002383{
2384 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002385 SendRawPECI(CrowApp& app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002386 Node(app,
2387 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2388 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002389 {
AppaRao Puli39460282020-04-07 17:03:04 +05302390 // Note: Deviated from redfish privilege registry for GET & HEAD
2391 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002392 entityPrivileges = {
2393 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2394 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2395 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2396 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2397 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2398 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2399 }
2400
2401 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002402 void doPost(crow::Response& res, const crow::Request& req,
2403 const std::vector<std::string>& params) override
Ed Tanous1da66f72018-07-27 16:13:37 -07002404 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002405 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002406 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002407
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002408 nlohmann::json reqJson =
2409 nlohmann::json::parse(req.body, nullptr, false);
2410 if (reqJson.find("PECICommands") != reqJson.end())
2411 {
2412 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2413 {
2414 return;
2415 }
2416 uint32_t idx = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002417 for (auto const& cmd : peciCommands)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002418 {
2419 if (cmd.size() < 3)
2420 {
2421 std::string s("[");
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002422 for (auto const& val : cmd)
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002423 {
2424 if (val != *cmd.begin())
2425 {
2426 s += ",";
2427 }
2428 s += std::to_string(val);
2429 }
2430 s += "]";
2431 messages::actionParameterValueFormatError(
2432 res, s, "PECICommands[" + std::to_string(idx) + "]",
2433 "SendRawPeci");
2434 return;
2435 }
2436 idx++;
2437 }
2438 }
2439 else
2440 {
2441 /* This interface is deprecated */
2442 uint8_t clientAddress = 0;
2443 uint8_t readLength = 0;
2444 std::vector<uint8_t> peciCommand;
2445 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2446 "ReadLength", readLength, "PECICommand",
2447 peciCommand))
2448 {
2449 return;
2450 }
2451 peciCommands.push_back({clientAddress, 0, readLength});
2452 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2453 peciCommand.end());
2454 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002455 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002456 auto sendRawPECICallback =
2457 [asyncResp](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002458 const std::vector<std::vector<uint8_t>>& resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002459 if (ec)
2460 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002461 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002462 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002463 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002464 return;
2465 }
2466 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2467 {"PECIResponse", resp}};
2468 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002469 // Call the SendRawPECI command with the provided data
2470 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002471 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002472 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002473 }
2474};
2475
Andrew Geisslercb92c032018-08-17 07:56:14 -07002476/**
2477 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2478 */
2479class DBusLogServiceActionsClear : public Node
2480{
2481 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002482 DBusLogServiceActionsClear(CrowApp& app) :
Andrew Geisslercb92c032018-08-17 07:56:14 -07002483 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002484 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002485 {
2486 entityPrivileges = {
2487 {boost::beast::http::verb::get, {{"Login"}}},
2488 {boost::beast::http::verb::head, {{"Login"}}},
2489 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2490 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2491 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2492 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2493 }
2494
2495 private:
2496 /**
2497 * Function handles POST method request.
2498 * The Clear Log actions does not require any parameter.The action deletes
2499 * all entries found in the Entries collection for this Log Service.
2500 */
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002501 void doPost(crow::Response& res, const crow::Request& req,
2502 const std::vector<std::string>& params) override
Andrew Geisslercb92c032018-08-17 07:56:14 -07002503 {
2504 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2505
2506 auto asyncResp = std::make_shared<AsyncResp>(res);
2507 // Process response from Logging service.
2508 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2509 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2510 if (ec)
2511 {
2512 // TODO Handle for specific error code
2513 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2514 asyncResp->res.result(
2515 boost::beast::http::status::internal_server_error);
2516 return;
2517 }
2518
2519 asyncResp->res.result(boost::beast::http::status::no_content);
2520 };
2521
2522 // Make call to Logging service to request Clear Log
2523 crow::connections::systemBus->async_method_call(
2524 resp_handler, "xyz.openbmc_project.Logging",
2525 "/xyz/openbmc_project/logging",
2526 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2527 }
2528};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002529
2530/****************************************************
2531 * Redfish PostCode interfaces
2532 * using DBUS interface: getPostCodesTS
2533 ******************************************************/
2534class PostCodesLogService : public Node
2535{
2536 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002537 PostCodesLogService(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002538 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2539 {
2540 entityPrivileges = {
2541 {boost::beast::http::verb::get, {{"Login"}}},
2542 {boost::beast::http::verb::head, {{"Login"}}},
2543 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2544 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2545 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2546 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2547 }
2548
2549 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002550 void doGet(crow::Response& res, const crow::Request& req,
2551 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002552 {
2553 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2554
2555 asyncResp->res.jsonValue = {
2556 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2557 {"@odata.type", "#LogService.v1_1_0.LogService"},
2558 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2559 {"Name", "POST Code Log Service"},
2560 {"Description", "POST Code Log Service"},
2561 {"Id", "BIOS POST Code Log"},
2562 {"OverWritePolicy", "WrapsWhenFull"},
2563 {"Entries",
2564 {{"@odata.id",
2565 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2566 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2567 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2568 "Actions/LogService.ClearLog"}};
2569 }
2570};
2571
2572class PostCodesClear : public Node
2573{
2574 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002575 PostCodesClear(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002576 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2577 "LogService.ClearLog/")
2578 {
2579 entityPrivileges = {
2580 {boost::beast::http::verb::get, {{"Login"}}},
2581 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302582 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2583 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2584 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2585 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002586 }
2587
2588 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002589 void doPost(crow::Response& res, const crow::Request& req,
2590 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002591 {
2592 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2593
2594 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2595 // Make call to post-code service to request clear all
2596 crow::connections::systemBus->async_method_call(
2597 [asyncResp](const boost::system::error_code ec) {
2598 if (ec)
2599 {
2600 // TODO Handle for specific error code
2601 BMCWEB_LOG_ERROR
2602 << "doClearPostCodes resp_handler got error " << ec;
2603 asyncResp->res.result(
2604 boost::beast::http::status::internal_server_error);
2605 messages::internalError(asyncResp->res);
2606 return;
2607 }
2608 },
2609 "xyz.openbmc_project.State.Boot.PostCode",
2610 "/xyz/openbmc_project/State/Boot/PostCode",
2611 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2612 }
2613};
2614
2615static void fillPostCodeEntry(
2616 std::shared_ptr<AsyncResp> aResp,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002617 const boost::container::flat_map<uint64_t, uint64_t>& postcode,
ZhikuiRena3316fc2020-01-29 14:58:08 -08002618 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2619 const uint64_t skip = 0, const uint64_t top = 0)
2620{
2621 // Get the Message from the MessageRegistry
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002622 const message_registries::Message* message =
ZhikuiRena3316fc2020-01-29 14:58:08 -08002623 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
ZhikuiRena3316fc2020-01-29 14:58:08 -08002624
2625 uint64_t currentCodeIndex = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002626 nlohmann::json& logEntryArray = aResp->res.jsonValue["Members"];
ZhikuiRena3316fc2020-01-29 14:58:08 -08002627
2628 uint64_t firstCodeTimeUs = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002629 for (const std::pair<uint64_t, uint64_t>& code : postcode)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002630 {
2631 currentCodeIndex++;
2632 std::string postcodeEntryID =
2633 "B" + std::to_string(bootIndex) + "-" +
2634 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2635
2636 uint64_t usecSinceEpoch = code.first;
2637 uint64_t usTimeOffset = 0;
2638
2639 if (1 == currentCodeIndex)
2640 { // already incremented
2641 firstCodeTimeUs = code.first;
2642 }
2643 else
2644 {
2645 usTimeOffset = code.first - firstCodeTimeUs;
2646 }
2647
2648 // skip if no specific codeIndex is specified and currentCodeIndex does
2649 // not fall between top and skip
2650 if ((codeIndex == 0) &&
2651 (currentCodeIndex <= skip || currentCodeIndex > top))
2652 {
2653 continue;
2654 }
2655
2656 // skip if a sepcific codeIndex is specified and does not match the
2657 // currentIndex
2658 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2659 {
2660 // This is done for simplicity. 1st entry is needed to calculate
2661 // time offset. To improve efficiency, one can get to the entry
2662 // directly (possibly with flatmap's nth method)
2663 continue;
2664 }
2665
2666 // currentCodeIndex is within top and skip or equal to specified code
2667 // index
2668
2669 // Get the Created time from the timestamp
2670 std::string entryTimeStr;
2671 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2672 {
2673 continue;
2674 }
2675
2676 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2677 std::ostringstream hexCode;
2678 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2679 << code.second;
2680 std::ostringstream timeOffsetStr;
2681 // Set Fixed -Point Notation
2682 timeOffsetStr << std::fixed;
2683 // Set precision to 4 digits
2684 timeOffsetStr << std::setprecision(4);
2685 // Add double to stream
2686 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2687 std::vector<std::string> messageArgs = {
2688 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2689
2690 // Get MessageArgs template from message registry
2691 std::string msg;
2692 if (message != nullptr)
2693 {
2694 msg = message->message;
2695
2696 // fill in this post code value
2697 int i = 0;
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002698 for (const std::string& messageArg : messageArgs)
ZhikuiRena3316fc2020-01-29 14:58:08 -08002699 {
2700 std::string argStr = "%" + std::to_string(++i);
2701 size_t argPos = msg.find(argStr);
2702 if (argPos != std::string::npos)
2703 {
2704 msg.replace(argPos, argStr.length(), messageArg);
2705 }
2706 }
2707 }
2708
Tim Leed4342a92020-04-27 11:47:58 +08002709 // Get Severity template from message registry
2710 std::string severity;
2711 if (message != nullptr)
2712 {
2713 severity = message->severity;
2714 }
2715
ZhikuiRena3316fc2020-01-29 14:58:08 -08002716 // add to AsyncResp
2717 logEntryArray.push_back({});
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002718 nlohmann::json& bmcLogEntry = logEntryArray.back();
ZhikuiRena3316fc2020-01-29 14:58:08 -08002719 bmcLogEntry = {
2720 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2721 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2722 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2723 "PostCodes/Entries/" +
2724 postcodeEntryID},
2725 {"Name", "POST Code Log Entry"},
2726 {"Id", postcodeEntryID},
2727 {"Message", std::move(msg)},
2728 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2729 {"MessageArgs", std::move(messageArgs)},
2730 {"EntryType", "Event"},
2731 {"Severity", std::move(severity)},
2732 {"Created", std::move(entryTimeStr)}};
2733 }
2734}
2735
2736static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2737 const uint16_t bootIndex,
2738 const uint64_t codeIndex)
2739{
2740 crow::connections::systemBus->async_method_call(
2741 [aResp, bootIndex, codeIndex](
2742 const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002743 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002744 if (ec)
2745 {
2746 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2747 messages::internalError(aResp->res);
2748 return;
2749 }
2750
2751 // skip the empty postcode boots
2752 if (postcode.empty())
2753 {
2754 return;
2755 }
2756
2757 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2758
2759 aResp->res.jsonValue["Members@odata.count"] =
2760 aResp->res.jsonValue["Members"].size();
2761 },
2762 "xyz.openbmc_project.State.Boot.PostCode",
2763 "/xyz/openbmc_project/State/Boot/PostCode",
2764 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2765 bootIndex);
2766}
2767
2768static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2769 const uint16_t bootIndex,
2770 const uint16_t bootCount,
2771 const uint64_t entryCount, const uint64_t skip,
2772 const uint64_t top)
2773{
2774 crow::connections::systemBus->async_method_call(
2775 [aResp, bootIndex, bootCount, entryCount, skip,
2776 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002777 const boost::container::flat_map<uint64_t, uint64_t>& postcode) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002778 if (ec)
2779 {
2780 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2781 messages::internalError(aResp->res);
2782 return;
2783 }
2784
2785 uint64_t endCount = entryCount;
2786 if (!postcode.empty())
2787 {
2788 endCount = entryCount + postcode.size();
2789
2790 if ((skip < endCount) && ((top + skip) > entryCount))
2791 {
2792 uint64_t thisBootSkip =
2793 std::max(skip, entryCount) - entryCount;
2794 uint64_t thisBootTop =
2795 std::min(top + skip, endCount) - entryCount;
2796
2797 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2798 thisBootSkip, thisBootTop);
2799 }
2800 aResp->res.jsonValue["Members@odata.count"] = endCount;
2801 }
2802
2803 // continue to previous bootIndex
2804 if (bootIndex < bootCount)
2805 {
2806 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2807 bootCount, endCount, skip, top);
2808 }
2809 else
2810 {
2811 aResp->res.jsonValue["Members@odata.nextLink"] =
2812 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2813 "Entries?$skip=" +
2814 std::to_string(skip + top);
2815 }
2816 },
2817 "xyz.openbmc_project.State.Boot.PostCode",
2818 "/xyz/openbmc_project/State/Boot/PostCode",
2819 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2820 bootIndex);
2821}
2822
2823static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2824 const uint64_t skip, const uint64_t top)
2825{
2826 uint64_t entryCount = 0;
2827 crow::connections::systemBus->async_method_call(
2828 [aResp, entryCount, skip,
2829 top](const boost::system::error_code ec,
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002830 const std::variant<uint16_t>& bootCount) {
ZhikuiRena3316fc2020-01-29 14:58:08 -08002831 if (ec)
2832 {
2833 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2834 messages::internalError(aResp->res);
2835 return;
2836 }
2837 auto pVal = std::get_if<uint16_t>(&bootCount);
2838 if (pVal)
2839 {
2840 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2841 }
2842 else
2843 {
2844 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2845 }
2846 },
2847 "xyz.openbmc_project.State.Boot.PostCode",
2848 "/xyz/openbmc_project/State/Boot/PostCode",
2849 "org.freedesktop.DBus.Properties", "Get",
2850 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2851}
2852
2853class PostCodesEntryCollection : public Node
2854{
2855 public:
2856 template <typename CrowApp>
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002857 PostCodesEntryCollection(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002858 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2859 {
2860 entityPrivileges = {
2861 {boost::beast::http::verb::get, {{"Login"}}},
2862 {boost::beast::http::verb::head, {{"Login"}}},
2863 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2864 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2865 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2866 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2867 }
2868
2869 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002870 void doGet(crow::Response& res, const crow::Request& req,
2871 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002872 {
2873 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2874
2875 asyncResp->res.jsonValue["@odata.type"] =
2876 "#LogEntryCollection.LogEntryCollection";
2877 asyncResp->res.jsonValue["@odata.context"] =
2878 "/redfish/v1/"
2879 "$metadata#LogEntryCollection.LogEntryCollection";
2880 asyncResp->res.jsonValue["@odata.id"] =
2881 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2882 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2883 asyncResp->res.jsonValue["Description"] =
2884 "Collection of POST Code Log Entries";
2885 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2886 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2887
2888 uint64_t skip = 0;
2889 uint64_t top = maxEntriesPerPage; // Show max entries by default
2890 if (!getSkipParam(asyncResp->res, req, skip))
2891 {
2892 return;
2893 }
2894 if (!getTopParam(asyncResp->res, req, top))
2895 {
2896 return;
2897 }
2898 getCurrentBootNumber(asyncResp, skip, top);
2899 }
2900};
2901
2902class PostCodesEntry : public Node
2903{
2904 public:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002905 PostCodesEntry(CrowApp& app) :
ZhikuiRena3316fc2020-01-29 14:58:08 -08002906 Node(app,
2907 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2908 std::string())
2909 {
2910 entityPrivileges = {
2911 {boost::beast::http::verb::get, {{"Login"}}},
2912 {boost::beast::http::verb::head, {{"Login"}}},
2913 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2914 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2915 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2916 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2917 }
2918
2919 private:
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002920 void doGet(crow::Response& res, const crow::Request& req,
2921 const std::vector<std::string>& params) override
ZhikuiRena3316fc2020-01-29 14:58:08 -08002922 {
2923 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2924 if (params.size() != 1)
2925 {
2926 messages::internalError(asyncResp->res);
2927 return;
2928 }
2929
Gunnar Mills1214b7e2020-06-04 10:11:30 -05002930 const std::string& targetID = params[0];
ZhikuiRena3316fc2020-01-29 14:58:08 -08002931
2932 size_t bootPos = targetID.find('B');
2933 if (bootPos == std::string::npos)
2934 {
2935 // Requested ID was not found
2936 messages::resourceMissingAtURI(asyncResp->res, targetID);
2937 return;
2938 }
2939 std::string_view bootIndexStr(targetID);
2940 bootIndexStr.remove_prefix(bootPos + 1);
2941 uint16_t bootIndex = 0;
2942 uint64_t codeIndex = 0;
2943 size_t dashPos = bootIndexStr.find('-');
2944
2945 if (dashPos == std::string::npos)
2946 {
2947 return;
2948 }
2949 std::string_view codeIndexStr(bootIndexStr);
2950 bootIndexStr.remove_suffix(dashPos);
2951 codeIndexStr.remove_prefix(dashPos + 1);
2952
2953 bootIndex = static_cast<uint16_t>(
2954 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2955 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2956 if (bootIndex == 0 || codeIndex == 0)
2957 {
2958 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2959 << params[0];
2960 }
2961
2962 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2963 asyncResp->res.jsonValue["@odata.context"] =
2964 "/redfish/v1/$metadata#LogEntry.LogEntry";
2965 asyncResp->res.jsonValue["@odata.id"] =
2966 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2967 "Entries";
2968 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2969 asyncResp->res.jsonValue["Description"] =
2970 "Collection of POST Code Log Entries";
2971 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2972 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2973
2974 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2975 }
2976};
2977
Ed Tanous1da66f72018-07-27 16:13:37 -07002978} // namespace redfish