blob: 79433b9a0ce9f7c067144c88d6d89d74ff261837 [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>
Andrew Geisslercb92c032018-08-17 07:56:14 -070030#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070031#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070032#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080033#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070034
35namespace redfish
36{
37
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070038constexpr char const *crashdumpObject = "com.intel.crashdump";
39constexpr char const *crashdumpPath = "/com/intel/crashdump";
40constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
41constexpr char const *crashdumpInterface = "com.intel.crashdump";
42constexpr char const *deleteAllInterface =
43 "xyz.openbmc_project.Collection.DeleteAll";
44constexpr char const *crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070045 "com.intel.crashdump.OnDemand";
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070046constexpr char const *crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070047 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070048
Jason M. Bills4851d452019-03-28 11:27:48 -070049namespace message_registries
50{
51static const Message *getMessageFromRegistry(
52 const std::string &messageKey,
53 const boost::beast::span<const MessageEntry> registry)
54{
55 boost::beast::span<const MessageEntry>::const_iterator messageIt =
56 std::find_if(registry.cbegin(), registry.cend(),
57 [&messageKey](const MessageEntry &messageEntry) {
58 return !std::strcmp(messageEntry.first,
59 messageKey.c_str());
60 });
61 if (messageIt != registry.cend())
62 {
63 return &messageIt->second;
64 }
65
66 return nullptr;
67}
68
69static const Message *getMessage(const std::string_view &messageID)
70{
71 // Redfish MessageIds are in the form
72 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
73 // the right Message
74 std::vector<std::string> fields;
75 fields.reserve(4);
76 boost::split(fields, messageID, boost::is_any_of("."));
77 std::string &registryName = fields[0];
78 std::string &messageKey = fields[3];
79
80 // Find the right registry and check it for the MessageKey
81 if (std::string(base::header.registryPrefix) == registryName)
82 {
83 return getMessageFromRegistry(
84 messageKey, boost::beast::span<const MessageEntry>(base::registry));
85 }
86 if (std::string(openbmc::header.registryPrefix) == registryName)
87 {
88 return getMessageFromRegistry(
89 messageKey,
90 boost::beast::span<const MessageEntry>(openbmc::registry));
91 }
92 return nullptr;
93}
94} // namespace message_registries
95
James Feistf6150402019-01-08 10:36:20 -080096namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070097
Andrew Geisslercb92c032018-08-17 07:56:14 -070098using GetManagedPropertyType = boost::container::flat_map<
Patrick Williams19bd78d2020-05-13 17:38:24 -050099 std::string, std::variant<std::string, bool, uint8_t, int16_t, uint16_t,
100 int32_t, uint32_t, int64_t, uint64_t, double>>;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700101
102using GetManagedObjectsType = boost::container::flat_map<
103 sdbusplus::message::object_path,
104 boost::container::flat_map<std::string, GetManagedPropertyType>>;
105
106inline std::string translateSeverityDbusToRedfish(const std::string &s)
107{
108 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
109 {
110 return "Critical";
111 }
112 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
113 {
114 return "Critical";
115 }
116 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
117 {
118 return "OK";
119 }
120 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
121 {
122 return "Critical";
123 }
124 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
125 {
126 return "Critical";
127 }
128 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
129 {
130 return "OK";
131 }
132 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
133 {
134 return "OK";
135 }
136 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
137 {
138 return "Warning";
139 }
140 return "";
141}
142
raviteja-bc9bb6862020-02-03 11:53:32 -0600143inline void deleteSystemDumpEntry(crow::Response &res,
144 const std::string &entryID)
145{
146 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
147
148 auto respHandler = [asyncResp](const boost::system::error_code ec) {
149 BMCWEB_LOG_DEBUG << "System Dump Entry doDelete callback: Done";
150 if (ec)
151 {
152 BMCWEB_LOG_ERROR
153 << "System Dump (DBus) doDelete respHandler got error " << ec;
154 asyncResp->res.result(
155 boost::beast::http::status::internal_server_error);
156 return;
157 }
158
159 asyncResp->res.result(boost::beast::http::status::ok);
160 };
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
Jason M. Bills16428a12018-11-02 12:42:29 -0700167static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800168 const std::string_view &field,
169 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700170{
171 const char *data = nullptr;
172 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(),
176 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
187static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800188 const std::string_view &field, const int &base,
Ed Tanous271584a2019-07-09 16:24:22 -0700189 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,
204 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);
Jason M. Bills16428a12018-11-02 12:42:29 -0700207 struct tm *loctime = localtime(&t);
208 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
ZhikuiRena3316fc2020-01-29 14:58:08 -0800225static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
226{
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
Jason M. Bills16428a12018-11-02 12:42:29 -0700239static bool getSkipParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700240 uint64_t &skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700241{
242 char *skipParam = req.urlParams.get("$skip");
243 if (skipParam != nullptr)
244 {
245 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;
Jason M. Bills16428a12018-11-02 12:42:29 -0700259static bool getTopParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700260 uint64_t &top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700261{
262 char *topParam = req.urlParams.get("$top");
263 if (topParam != nullptr)
264 {
265 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
Jason M. Billse85d6b12019-07-29 17:01:15 -0700285static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
286 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
Jason M. Billse85d6b12019-07-29 17:01:15 -0700326static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
327 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
Jason M. Bills16428a12018-11-02 12:42:29 -0700365static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
Ed Tanous271584a2019-07-09 16:24:22 -0700366 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 }
Ed Tanous271584a2019-07-09 16:24:22 -0700387 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700388 {
389 messages::resourceMissingAtURI(res, entryID);
390 return false;
391 }
Ed Tanous271584a2019-07-09 16:24:22 -0700392 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 }
Ed Tanous271584a2019-07-09 16:24:22 -0700409 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700410 {
411 messages::resourceMissingAtURI(res, entryID);
412 return false;
413 }
Ed Tanous271584a2019-07-09 16:24:22 -0700414 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
428 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
429{
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
434 for (const std::filesystem::directory_entry &dirEnt :
435 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(
453 const std::vector<std::pair<std::string, VariantType>> &params,
454 std::string &filename, std::string &timestamp, std::string &logfile)
455{
456 for (auto property : params)
457 {
458 if (property.first == "Timestamp")
459 {
460 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 {
469 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 {
478 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
ZhikuiRena3316fc2020-01-29 14:58:08 -0800488constexpr 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>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800493 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 */
509 void doGet(crow::Response &res, const crow::Request &req,
510 const std::vector<std::string> &params) override
511 {
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";
522 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
523 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,
541 const std::vector<std::string> &subtreePath) {
542 if (ec)
543 {
544 BMCWEB_LOG_ERROR << ec;
545 return;
546 }
547
548 for (auto &pathStr : subtreePath)
549 {
550 if (pathStr.find("PostCode") != std::string::npos)
551 {
552 nlohmann::json &logServiceArray =
553 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,
566 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>
574 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:
587 void doGet(crow::Response &res, const crow::Request &req,
588 const std::vector<std::string> &params) override
589 {
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:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800613 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:
627 void doPost(crow::Response &res, const crow::Request &req,
628 const std::vector<std::string> &params) override
629 {
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 {
636 for (const std::filesystem::path &file : redfishLogFiles)
637 {
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
Jason M. Bills95820182019-04-22 16:25:34 -0700661static int fillEventLogEntryJson(const std::string &logEntryID,
662 const std::string logEntry,
663 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 }
690 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
693 const message_registries::Message *message =
694 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 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700708 std::string &messageArgsStart = logEntryFields[1];
709 // 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;
720 for (const std::string &messageArg : messageArgs)
721 {
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>
Anthony Wilson27062602019-04-22 02:10:09 -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:
775 void doGet(crow::Response &res, const crow::Request &req,
776 const std::vector<std::string> &params) override
777 {
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
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800799 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
800 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({});
841 nlohmann::json &bmcLogEntry = logEntryArray.back();
842 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:
863 JournalEventLogEntry(CrowApp &app) :
864 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:
878 void doGet(crow::Response &res, const crow::Request &req,
879 const std::vector<std::string> &params) override
880 {
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 }
887 const std::string &targetID = params[0];
888
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>
941 DBusEventLogEntryCollection(CrowApp &app) :
942 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:
954 void doGet(crow::Response &res, const crow::Request &req,
955 const std::vector<std::string> &params) override
956 {
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,
973 GetManagedObjectsType &resp) {
974 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 }
983 nlohmann::json &entriesArray =
984 asyncResp->res.jsonValue["Members"];
985 entriesArray = nlohmann::json::array();
986 for (auto &objectPath : resp)
987 {
988 for (auto &interfaceMap : objectPath.second)
989 {
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({});
998 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -0700999 uint32_t *id = nullptr;
1000 std::time_t timestamp{};
1001 std::string *severity = nullptr;
1002 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001003 for (auto &propertyMap : interfaceMap.second)
1004 {
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 {
1016 const uint64_t *millisTimeStamp =
1017 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(),
1070 [](const nlohmann::json &left,
1071 const nlohmann::json &right) {
1072 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:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -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:
1100 void doGet(crow::Response &res, const crow::Request &req,
1101 const std::vector<std::string> &params) override
1102 {
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 }
Ed Tanous029573d2019-02-01 10:57:49 -08001109 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,
1115 GetManagedPropertyType &resp) {
1116 if (ec)
1117 {
1118 BMCWEB_LOG_ERROR
1119 << "EventLogEntry (DBus) resp_handler got error " << ec;
1120 messages::internalError(asyncResp->res);
1121 return;
1122 }
Ed Tanous66664f22019-10-11 13:05:49 -07001123 uint32_t *id = nullptr;
1124 std::time_t timestamp{};
1125 std::string *severity = nullptr;
1126 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001127 for (auto &propertyMap : resp)
1128 {
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 {
1139 const uint64_t *millisTimeStamp =
1140 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
1199 void doDelete(crow::Response &res, const crow::Request &req,
1200 const std::vector<std::string> &params) override
1201 {
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>
1245 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 */
1261 void doGet(crow::Response &res, const crow::Request &req,
1262 const std::vector<std::string> &params) override
1263 {
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";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001274 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1275 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>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001289 BMCJournalLogService(CrowApp &app) :
1290 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:
1302 void doGet(crow::Response &res, const crow::Request &req,
1303 const std::vector<std::string> &params) override
1304 {
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
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001320static 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>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001370 BMCJournalLogEntryCollection(CrowApp &app) :
1371 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:
1383 void doGet(crow::Response &res, const crow::Request &req,
1384 const std::vector<std::string> &params) override
1385 {
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";
Jason M. Billse1f26342018-07-18 12:12:00 -07001411 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1412 logEntryArray = nlohmann::json::array();
1413
1414 // Go through the journal and use the timestamp to create a unique ID
1415 // for each entry
1416 sd_journal *journalTmp = nullptr;
1417 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({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001452 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1453 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:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001473 BMCJournalLogEntry(CrowApp &app) :
1474 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:
1487 void doGet(crow::Response &res, const crow::Request &req,
1488 const std::vector<std::string> &params) override
1489 {
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 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001496 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
1505 sd_journal *journalTmp = nullptr;
1506 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);
Ed Tanous271584a2019-07-09 16:24:22 -07001521 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001522 {
1523 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001524 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1525 {
1526 messages::internalError(asyncResp->res);
1527 return;
1528 }
1529 if (firstEntry)
1530 {
1531 firstEntry = false;
1532 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001533 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001534 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001535 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001536 {
1537 messages::resourceMissingAtURI(asyncResp->res, entryID);
1538 return;
1539 }
1540
1541 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1542 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001543 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001544 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001545 return;
1546 }
1547 }
1548};
1549
raviteja-bc9bb6862020-02-03 11:53:32 -06001550class SystemDumpService : public Node
1551{
1552 public:
1553 template <typename CrowApp>
1554 SystemDumpService(CrowApp &app) :
1555 Node(app, "/redfish/v1/Systems/system/LogServices/System/")
1556 {
1557 entityPrivileges = {
1558 {boost::beast::http::verb::get, {{"Login"}}},
1559 {boost::beast::http::verb::head, {{"Login"}}},
1560 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1561 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1562 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1563 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1564 }
1565
1566 private:
1567 void doGet(crow::Response &res, const crow::Request &req,
1568 const std::vector<std::string> &params) override
1569 {
1570 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1571
1572 asyncResp->res.jsonValue["@odata.id"] =
1573 "/redfish/v1/Systems/system/LogServices/System";
1574 asyncResp->res.jsonValue["@odata.type"] =
1575 "#LogService.v1_1_0.LogService";
1576 asyncResp->res.jsonValue["Name"] = "Dump Log Service";
1577 asyncResp->res.jsonValue["Description"] = "System Dump Log Service";
1578 asyncResp->res.jsonValue["Id"] = "System";
1579 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1580 asyncResp->res.jsonValue["LogEntryTypes"] = "Dump";
1581 asyncResp->res.jsonValue["Oem"]["DumpType"] = "System";
1582
1583 asyncResp->res.jsonValue["Entries"] = {
1584 {"@odata.id",
1585 "/redfish/v1/Systems/system/LogServices/System/Entries"}};
1586 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
1587 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1588 "Actions/LogService.ClearLog"}};
1589 asyncResp->res.jsonValue["Actions"]["#LogService.CreateLog"] = {
1590 {"target", "/redfish/v1/Systems/system/LogServices/System/"
1591 "Actions/LogService.CreateLog"}};
1592 }
1593};
1594
1595class SystemDumpEntryCollection : public Node
1596{
1597 public:
1598 template <typename CrowApp>
1599 SystemDumpEntryCollection(CrowApp &app) :
1600 Node(app, "/redfish/v1/Systems/system/LogServices/System/Entries/")
1601 {
1602 entityPrivileges = {
1603 {boost::beast::http::verb::get, {{"Login"}}},
1604 {boost::beast::http::verb::head, {{"Login"}}},
1605 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1606 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1607 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1608 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1609 }
1610
1611 private:
1612 /**
1613 * Functions triggers appropriate requests on DBus
1614 */
1615 void doGet(crow::Response &res, const crow::Request &req,
1616 const std::vector<std::string> &params) override
1617 {
1618 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1619
1620 asyncResp->res.jsonValue["@odata.type"] =
1621 "#LogEntryCollection.LogEntryCollection";
1622 asyncResp->res.jsonValue["@odata.id"] =
1623 "/redfish/v1/Systems/system/LogServices/System/Entries";
1624 asyncResp->res.jsonValue["Name"] = "System Dump Entries";
1625 asyncResp->res.jsonValue["Description"] =
1626 "Collection of System Dump Entries";
1627
1628 crow::connections::systemBus->async_method_call(
1629 [asyncResp](const boost::system::error_code ec,
1630 const crow::openbmc_mapper::GetSubTreeType &resp) {
1631 if (ec)
1632 {
1633 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1634 messages::internalError(asyncResp->res);
1635 return;
1636 }
1637
1638 nlohmann::json &logArray = asyncResp->res.jsonValue["Members"];
1639 logArray = nlohmann::json::array();
1640 for (auto &object : resp)
1641 {
1642 const std::string &path =
1643 static_cast<const std::string &>(object.first);
1644 std::size_t lastPos = path.rfind("/");
1645 if (lastPos == std::string::npos)
1646 {
1647 continue;
1648 }
1649 std::string logID = path.substr(lastPos + 1);
1650 logArray.push_back(
1651 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
1652 "System/Entries/" +
1653 logID}});
1654 }
1655 asyncResp->res.jsonValue["Members@odata.count"] =
1656 logArray.size();
1657 },
1658 "xyz.openbmc_project.ObjectMapper",
1659 "/xyz/openbmc_project/object_mapper",
1660 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1661 "/xyz/openbmc_project/dump", 0,
1662 std::array<const char *, 1>{
1663 "xyz.openbmc_project.Dump.Entry.System"});
1664 }
1665};
1666
1667class SystemDumpEntry : public Node
1668{
1669 public:
1670 SystemDumpEntry(CrowApp &app) :
1671 Node(app,
1672 "/redfish/v1/Systems/system/LogServices/System/Entries/<str>/",
1673 std::string())
1674 {
1675 entityPrivileges = {
1676 {boost::beast::http::verb::get, {{"Login"}}},
1677 {boost::beast::http::verb::head, {{"Login"}}},
1678 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1679 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1680 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1681 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1682 }
1683
1684 private:
1685 void doGet(crow::Response &res, const crow::Request &req,
1686 const std::vector<std::string> &params) override
1687 {
1688 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1689 if (params.size() != 1)
1690 {
1691 messages::internalError(asyncResp->res);
1692 return;
1693 }
1694 const std::string &entryID = params[0];
1695 crow::connections::systemBus->async_method_call(
1696 [asyncResp, entryID](const boost::system::error_code ec,
1697 GetManagedObjectsType &resp) {
1698 if (ec)
1699 {
1700 BMCWEB_LOG_ERROR
1701 << "SystemDumpEntry resp_handler got error " << ec;
1702 messages::internalError(asyncResp->res);
1703 return;
1704 }
1705
1706 for (auto &objectPath : resp)
1707 {
1708 if (objectPath.first.str.find(
1709 "/xyz/openbmc_project/dump/entry/" + entryID) ==
1710 std::string::npos)
1711 {
1712 continue;
1713 }
1714
1715 bool foundSystemDumpEntry = false;
1716 for (auto &interfaceMap : objectPath.second)
1717 {
1718 if (interfaceMap.first ==
1719 "xyz.openbmc_project.Dump.Entry.System")
1720 {
1721 foundSystemDumpEntry = true;
1722 break;
1723 }
1724 }
1725 if (foundSystemDumpEntry == false)
1726 {
1727 BMCWEB_LOG_DEBUG << "Can't find System Dump Entry";
1728 messages::internalError(asyncResp->res);
1729 return;
1730 }
1731
1732 std::string timestamp{};
1733 uint64_t size = 0;
1734
1735 for (auto &interfaceMap : objectPath.second)
1736 {
1737 if (interfaceMap.first ==
1738 "xyz.openbmc_project.Dump.Entry")
1739 {
1740 for (auto &propertyMap : interfaceMap.second)
1741 {
1742 if (propertyMap.first == "Size")
1743 {
1744 auto sizePtr = std::get_if<uint64_t>(
1745 &propertyMap.second);
1746 if (sizePtr == nullptr)
1747 {
1748 messages::propertyMissing(
1749 asyncResp->res, "Size");
1750 break;
1751 }
1752 size = *sizePtr;
1753 break;
1754 }
1755 }
1756 }
1757 else if (interfaceMap.first ==
1758 "xyz.openbmc_project.Time.EpochTime")
1759 {
1760 for (auto &propertyMap : interfaceMap.second)
1761 {
1762 if (propertyMap.first == "Elapsed")
1763 {
1764 const uint64_t *usecsTimeStamp =
1765 std::get_if<uint64_t>(
1766 &propertyMap.second);
1767 if (usecsTimeStamp == nullptr)
1768 {
1769 messages::propertyMissing(
1770 asyncResp->res, "Elapsed");
1771 break;
1772 }
1773 getTimestampStr(*usecsTimeStamp, timestamp);
1774 break;
1775 }
1776 }
1777 }
1778 }
1779 asyncResp->res.jsonValue = {
1780 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1781 {"@odata.id",
1782 "/redfish/v1/Systems/system/LogServices/System/"
1783 "Entries/" +
1784 entryID},
1785 {"Name", "System Dump Entry"},
1786 {"Id", entryID},
1787 {"SizeInB", size},
1788 {"EntryType", "Dump"},
1789 {"EntryCode", "User generated dump"},
1790 {"Created", timestamp}};
1791
1792 asyncResp->res
1793 .jsonValue["Actions"]["#LogEntry.DownloadLog"] = {
1794 {"target",
1795 "/redfish/v1/Systems/system/LogServices/System/"
1796 "Entries/" +
1797 entryID + "/Actions/LogEntry.DownloadLog"}};
1798 }
1799 },
1800 "xyz.openbmc_project.Dump.Manager", "/xyz/openbmc_project/dump",
1801 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
1802 }
1803
1804 void doDelete(crow::Response &res, const crow::Request &req,
1805 const std::vector<std::string> &params) override
1806 {
1807 BMCWEB_LOG_DEBUG << "Do delete single dump entry";
1808
1809 auto asyncResp = std::make_shared<AsyncResp>(res);
1810
1811 if (params.size() != 1)
1812 {
1813 messages::internalError(asyncResp->res);
1814 return;
1815 }
1816 std::string entryID = params[0];
1817
1818 crow::connections::systemBus->async_method_call(
1819 [asyncResp,
1820 entryID](const boost::system::error_code ec,
1821 const crow::openbmc_mapper::GetSubTreeType &resp) {
1822 if (ec)
1823 {
1824 BMCWEB_LOG_ERROR << " resp_handler got error " << ec;
1825 messages::internalError(asyncResp->res);
1826 return;
1827 }
1828
1829 for (auto &object : resp)
1830 {
1831 const std::string &path =
1832 static_cast<const std::string &>(object.first);
1833
1834 std::size_t pos = path.rfind(
1835 "/xyz/openbmc_project/dump/entry/" + entryID);
1836 if (pos != std::string::npos)
1837 {
1838 deleteSystemDumpEntry(asyncResp->res, entryID);
1839 return;
1840 }
1841 }
1842 },
1843 "xyz.openbmc_project.ObjectMapper",
1844 "/xyz/openbmc_project/object_mapper",
1845 "xyz.openbmc_project.ObjectMapper", "GetSubTree",
1846 "/xyz/openbmc_project/dump", 0,
1847 std::array<const char *, 1>{
1848 "xyz.openbmc_project.Dump.Entry.System"});
1849 }
1850};
1851
Jason M. Bills424c4172019-03-21 13:50:33 -07001852class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001853{
1854 public:
1855 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001856 CrashdumpService(CrowApp &app) :
1857 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001858 {
AppaRao Puli39460282020-04-07 17:03:04 +05301859 // Note: Deviated from redfish privilege registry for GET & HEAD
1860 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07001861 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301862 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1863 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07001864 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1865 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1866 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1867 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001868 }
1869
1870 private:
1871 /**
1872 * Functions triggers appropriate requests on DBus
1873 */
1874 void doGet(crow::Response &res, const crow::Request &req,
1875 const std::vector<std::string> &params) override
1876 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001877 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001878 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001879 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001880 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001881 asyncResp->res.jsonValue["@odata.type"] =
1882 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001883 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1884 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1885 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001886 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1887 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001888 asyncResp->res.jsonValue["Entries"] = {
1889 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001890 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001891 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001892 {"#LogService.ClearLog",
1893 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1894 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001895 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001896 {{"#Crashdump.OnDemand",
1897 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1898 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001899
1900#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001901 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001902 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001903 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1904 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001905#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001906 }
1907};
1908
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001909class CrashdumpClear : public Node
1910{
1911 public:
1912 CrashdumpClear(CrowApp &app) :
1913 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1914 "LogService.ClearLog/")
1915 {
AppaRao Puli39460282020-04-07 17:03:04 +05301916 // Note: Deviated from redfish privilege registry for GET & HEAD
1917 // method for security reasons.
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001918 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05301919 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1920 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001921 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1922 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1923 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1924 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1925 }
1926
1927 private:
1928 void doPost(crow::Response &res, const crow::Request &req,
1929 const std::vector<std::string> &params) override
1930 {
1931 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1932
1933 crow::connections::systemBus->async_method_call(
1934 [asyncResp](const boost::system::error_code ec,
1935 const std::string &resp) {
1936 if (ec)
1937 {
1938 messages::internalError(asyncResp->res);
1939 return;
1940 }
1941 messages::success(asyncResp->res);
1942 },
1943 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
1944 }
1945};
1946
Jason M. Billse855dd22019-10-08 11:37:48 -07001947static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1948 const std::string &logID,
1949 nlohmann::json &logEntryJson)
1950{
Johnathan Mantey043a0532020-03-10 17:15:28 -07001951 auto getStoredLogCallback =
1952 [asyncResp, logID, &logEntryJson](
1953 const boost::system::error_code ec,
1954 const std::vector<std::pair<std::string, VariantType>> &params) {
1955 if (ec)
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001956 {
Johnathan Mantey043a0532020-03-10 17:15:28 -07001957 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1958 if (ec.value() ==
1959 boost::system::linux_error::bad_request_descriptor)
1960 {
1961 messages::resourceNotFound(asyncResp->res, "LogEntry",
1962 logID);
1963 }
1964 else
1965 {
1966 messages::internalError(asyncResp->res);
1967 }
1968 return;
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001969 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001970
Johnathan Mantey043a0532020-03-10 17:15:28 -07001971 std::string timestamp{};
1972 std::string filename{};
1973 std::string logfile{};
1974 ParseCrashdumpParameters(params, filename, timestamp, logfile);
1975
1976 if (filename.empty() || timestamp.empty())
1977 {
1978 messages::resourceMissingAtURI(asyncResp->res, logID);
1979 return;
1980 }
1981
1982 std::string crashdumpURI =
1983 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1984 logID + "/" + filename;
1985 logEntryJson = {{"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1986 {"@odata.id", "/redfish/v1/Systems/system/"
1987 "LogServices/Crashdump/Entries/" +
1988 logID},
1989 {"Name", "CPU Crashdump"},
1990 {"Id", logID},
1991 {"EntryType", "Oem"},
1992 {"OemRecordFormat", "Crashdump URI"},
1993 {"Message", std::move(crashdumpURI)},
1994 {"Created", std::move(timestamp)}};
1995 };
Jason M. Billse855dd22019-10-08 11:37:48 -07001996 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001997 std::move(getStoredLogCallback), crashdumpObject,
1998 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07001999 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Jason M. Billse855dd22019-10-08 11:37:48 -07002000}
2001
Jason M. Bills424c4172019-03-21 13:50:33 -07002002class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002003{
2004 public:
2005 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07002006 CrashdumpEntryCollection(CrowApp &app) :
2007 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002008 {
AppaRao Puli39460282020-04-07 17:03:04 +05302009 // Note: Deviated from redfish privilege registry for GET & HEAD
2010 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002011 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302012 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2013 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002014 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2015 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2016 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2017 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002018 }
2019
2020 private:
2021 /**
2022 * Functions triggers appropriate requests on DBus
2023 */
2024 void doGet(crow::Response &res, const crow::Request &req,
2025 const std::vector<std::string> &params) override
2026 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002027 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002028 // Collections don't include the static data added by SubRoute because
2029 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07002030 auto getLogEntriesCallback = [asyncResp](
2031 const boost::system::error_code ec,
2032 const std::vector<std::string> &resp) {
2033 if (ec)
2034 {
2035 if (ec.value() !=
2036 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07002037 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002038 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
2039 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002040 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002041 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07002042 }
Jason M. Billse1f26342018-07-18 12:12:00 -07002043 }
2044 asyncResp->res.jsonValue["@odata.type"] =
2045 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08002046 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002047 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07002048 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002049 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07002050 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07002051 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
2052 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07002053 std::vector<std::string> logIDs;
2054 // Get the list of log entries and build up an empty array big
2055 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07002056 for (const std::string &objpath : resp)
2057 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002058 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07002059 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07002060 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07002061 {
Jason M. Billse855dd22019-10-08 11:37:48 -07002062 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07002063 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002064 logIDs.emplace_back(objpath.substr(lastPos + 1));
2065
2066 // Add a space for the log entry to the array
2067 logEntryArray.push_back({});
2068 }
2069 // Now go through and set up async calls to fill in the entries
2070 size_t index = 0;
2071 for (const std::string &logID : logIDs)
2072 {
2073 // Add the log entry to the array
2074 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07002075 }
2076 asyncResp->res.jsonValue["Members@odata.count"] =
2077 logEntryArray.size();
2078 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002079 crow::connections::systemBus->async_method_call(
2080 std::move(getLogEntriesCallback),
2081 "xyz.openbmc_project.ObjectMapper",
2082 "/xyz/openbmc_project/object_mapper",
2083 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002084 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07002085 }
2086};
2087
Jason M. Bills424c4172019-03-21 13:50:33 -07002088class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002089{
2090 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002091 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08002092 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07002093 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07002094 std::string())
2095 {
AppaRao Puli39460282020-04-07 17:03:04 +05302096 // Note: Deviated from redfish privilege registry for GET & HEAD
2097 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002098 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302099 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2100 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse1f26342018-07-18 12:12:00 -07002101 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2102 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2103 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2104 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002105 }
2106
2107 private:
2108 void doGet(crow::Response &res, const crow::Request &req,
2109 const std::vector<std::string> &params) override
2110 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002111 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002112 if (params.size() != 1)
2113 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07002114 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002115 return;
2116 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002117 const std::string &logID = params[0];
2118 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2119 }
2120};
2121
2122class CrashdumpFile : public Node
2123{
2124 public:
2125 CrashdumpFile(CrowApp &app) :
2126 Node(app,
2127 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
2128 "<str>/",
2129 std::string(), std::string())
2130 {
AppaRao Puli39460282020-04-07 17:03:04 +05302131 // Note: Deviated from redfish privilege registry for GET & HEAD
2132 // method for security reasons.
Jason M. Billse855dd22019-10-08 11:37:48 -07002133 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302134 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2135 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
Jason M. Billse855dd22019-10-08 11:37:48 -07002136 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2137 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2138 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2139 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2140 }
2141
2142 private:
2143 void doGet(crow::Response &res, const crow::Request &req,
2144 const std::vector<std::string> &params) override
2145 {
2146 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2147 if (params.size() != 2)
2148 {
2149 messages::internalError(asyncResp->res);
2150 return;
2151 }
2152 const std::string &logID = params[0];
2153 const std::string &fileName = params[1];
2154
Johnathan Mantey043a0532020-03-10 17:15:28 -07002155 auto getStoredLogCallback =
2156 [asyncResp, logID, fileName](
2157 const boost::system::error_code ec,
2158 const std::vector<std::pair<std::string, VariantType>> &resp) {
2159 if (ec)
2160 {
2161 BMCWEB_LOG_DEBUG << "failed to get log ec: "
2162 << ec.message();
2163 messages::internalError(asyncResp->res);
2164 return;
2165 }
Jason M. Billse855dd22019-10-08 11:37:48 -07002166
Johnathan Mantey043a0532020-03-10 17:15:28 -07002167 std::string dbusFilename{};
2168 std::string dbusTimestamp{};
2169 std::string dbusFilepath{};
Jason M. Billse855dd22019-10-08 11:37:48 -07002170
Johnathan Mantey043a0532020-03-10 17:15:28 -07002171 ParseCrashdumpParameters(resp, dbusFilename, dbusTimestamp,
2172 dbusFilepath);
2173
2174 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2175 dbusFilepath.empty())
2176 {
2177 messages::resourceMissingAtURI(asyncResp->res, fileName);
2178 return;
2179 }
2180
2181 // Verify the file name parameter is correct
2182 if (fileName != dbusFilename)
2183 {
2184 messages::resourceMissingAtURI(asyncResp->res, fileName);
2185 return;
2186 }
2187
2188 if (!std::filesystem::exists(dbusFilepath))
2189 {
2190 messages::resourceMissingAtURI(asyncResp->res, fileName);
2191 return;
2192 }
2193 std::ifstream ifs(dbusFilepath, std::ios::in |
2194 std::ios::binary |
2195 std::ios::ate);
2196 std::ifstream::pos_type fileSize = ifs.tellg();
2197 if (fileSize < 0)
2198 {
2199 messages::generalError(asyncResp->res);
2200 return;
2201 }
2202 ifs.seekg(0, std::ios::beg);
2203
2204 auto crashData = std::make_unique<char[]>(
2205 static_cast<unsigned int>(fileSize));
2206
2207 ifs.read(crashData.get(), static_cast<int>(fileSize));
2208
2209 // The cast to std::string is intentional in order to use the
2210 // assign() that applies move mechanics
2211 asyncResp->res.body().assign(
2212 static_cast<std::string>(crashData.get()));
2213
2214 // Configure this to be a file download when accessed from
2215 // a browser
2216 asyncResp->res.addHeader("Content-Disposition", "attachment");
2217 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002218 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002219 std::move(getStoredLogCallback), crashdumpObject,
2220 crashdumpPath + std::string("/") + logID,
Johnathan Mantey043a0532020-03-10 17:15:28 -07002221 "org.freedesktop.DBus.Properties", "GetAll", crashdumpInterface);
Ed Tanous1da66f72018-07-27 16:13:37 -07002222 }
2223};
2224
Jason M. Bills424c4172019-03-21 13:50:33 -07002225class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002226{
2227 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07002228 OnDemandCrashdump(CrowApp &app) :
2229 Node(app,
2230 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2231 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002232 {
AppaRao Puli39460282020-04-07 17:03:04 +05302233 // Note: Deviated from redfish privilege registry for GET & HEAD
2234 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002235 entityPrivileges = {
AppaRao Puli39460282020-04-07 17:03:04 +05302236 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2237 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2238 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2239 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2240 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2241 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07002242 }
2243
2244 private:
2245 void doPost(crow::Response &res, const crow::Request &req,
2246 const std::vector<std::string> &params) override
2247 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002248 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002249
James Feistfe306722020-03-12 16:32:08 -07002250 auto generateonDemandLogCallback = [asyncResp,
2251 req](const boost::system::error_code
2252 ec,
2253 const std::string &resp) {
James Feist46229572020-02-19 15:11:58 -08002254 if (ec)
2255 {
2256 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07002257 {
James Feist46229572020-02-19 15:11:58 -08002258 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07002259 }
James Feist46229572020-02-19 15:11:58 -08002260 else if (ec.value() ==
2261 boost::system::errc::device_or_resource_busy)
2262 {
2263 messages::serviceTemporarilyUnavailable(asyncResp->res,
2264 "60");
2265 }
2266 else
2267 {
2268 messages::internalError(asyncResp->res);
2269 }
2270 return;
2271 }
2272 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08002273 [](boost::system::error_code err, sdbusplus::message::message &,
2274 const std::shared_ptr<task::TaskData> &taskData) {
2275 if (!err)
2276 {
James Feiste5d50062020-05-11 17:29:00 -07002277 taskData->messages.emplace_back(
2278 messages::taskCompletedOK(
2279 std::to_string(taskData->index)));
James Feist831d6b02020-03-12 16:31:30 -07002280 taskData->state = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08002281 }
James Feist32898ce2020-03-10 16:16:52 -07002282 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08002283 },
James Feist46229572020-02-19 15:11:58 -08002284 "type='signal',interface='org.freedesktop.DBus.Properties',"
2285 "member='PropertiesChanged',arg0namespace='com.intel."
2286 "crashdump'");
2287 task->startTimer(std::chrono::minutes(5));
2288 task->populateResp(asyncResp->res);
James Feistfe306722020-03-12 16:32:08 -07002289 task->payload.emplace(req);
James Feist46229572020-02-19 15:11:58 -08002290 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002291 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002292 std::move(generateonDemandLogCallback), crashdumpObject,
2293 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07002294 }
2295};
2296
Jason M. Billse1f26342018-07-18 12:12:00 -07002297class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07002298{
2299 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07002300 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07002301 Node(app,
2302 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
2303 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07002304 {
AppaRao Puli39460282020-04-07 17:03:04 +05302305 // Note: Deviated from redfish privilege registry for GET & HEAD
2306 // method for security reasons.
Ed Tanous1da66f72018-07-27 16:13:37 -07002307 entityPrivileges = {
2308 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
2309 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
2310 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2311 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2312 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2313 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
2314 }
2315
2316 private:
2317 void doPost(crow::Response &res, const crow::Request &req,
2318 const std::vector<std::string> &params) override
2319 {
Jason M. Billse1f26342018-07-18 12:12:00 -07002320 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002321 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07002322
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002323 nlohmann::json reqJson =
2324 nlohmann::json::parse(req.body, nullptr, false);
2325 if (reqJson.find("PECICommands") != reqJson.end())
2326 {
2327 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
2328 {
2329 return;
2330 }
2331 uint32_t idx = 0;
2332 for (auto const &cmd : peciCommands)
2333 {
2334 if (cmd.size() < 3)
2335 {
2336 std::string s("[");
2337 for (auto const &val : cmd)
2338 {
2339 if (val != *cmd.begin())
2340 {
2341 s += ",";
2342 }
2343 s += std::to_string(val);
2344 }
2345 s += "]";
2346 messages::actionParameterValueFormatError(
2347 res, s, "PECICommands[" + std::to_string(idx) + "]",
2348 "SendRawPeci");
2349 return;
2350 }
2351 idx++;
2352 }
2353 }
2354 else
2355 {
2356 /* This interface is deprecated */
2357 uint8_t clientAddress = 0;
2358 uint8_t readLength = 0;
2359 std::vector<uint8_t> peciCommand;
2360 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
2361 "ReadLength", readLength, "PECICommand",
2362 peciCommand))
2363 {
2364 return;
2365 }
2366 peciCommands.push_back({clientAddress, 0, readLength});
2367 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
2368 peciCommand.end());
2369 }
Ed Tanous1da66f72018-07-27 16:13:37 -07002370 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07002371 auto sendRawPECICallback =
2372 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002373 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07002374 if (ec)
2375 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002376 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07002377 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07002378 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07002379 return;
2380 }
2381 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
2382 {"PECIResponse", resp}};
2383 };
Ed Tanous1da66f72018-07-27 16:13:37 -07002384 // Call the SendRawPECI command with the provided data
2385 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07002386 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08002387 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07002388 }
2389};
2390
Andrew Geisslercb92c032018-08-17 07:56:14 -07002391/**
2392 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
2393 */
2394class DBusLogServiceActionsClear : public Node
2395{
2396 public:
2397 DBusLogServiceActionsClear(CrowApp &app) :
2398 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Gunnar Mills7af91512020-04-14 22:16:57 -05002399 "LogService.ClearLog/")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002400 {
2401 entityPrivileges = {
2402 {boost::beast::http::verb::get, {{"Login"}}},
2403 {boost::beast::http::verb::head, {{"Login"}}},
2404 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2405 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2406 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2407 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2408 }
2409
2410 private:
2411 /**
2412 * Function handles POST method request.
2413 * The Clear Log actions does not require any parameter.The action deletes
2414 * all entries found in the Entries collection for this Log Service.
2415 */
2416 void doPost(crow::Response &res, const crow::Request &req,
2417 const std::vector<std::string> &params) override
2418 {
2419 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2420
2421 auto asyncResp = std::make_shared<AsyncResp>(res);
2422 // Process response from Logging service.
2423 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2424 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2425 if (ec)
2426 {
2427 // TODO Handle for specific error code
2428 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2429 asyncResp->res.result(
2430 boost::beast::http::status::internal_server_error);
2431 return;
2432 }
2433
2434 asyncResp->res.result(boost::beast::http::status::no_content);
2435 };
2436
2437 // Make call to Logging service to request Clear Log
2438 crow::connections::systemBus->async_method_call(
2439 resp_handler, "xyz.openbmc_project.Logging",
2440 "/xyz/openbmc_project/logging",
2441 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2442 }
2443};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002444
2445/****************************************************
2446 * Redfish PostCode interfaces
2447 * using DBUS interface: getPostCodesTS
2448 ******************************************************/
2449class PostCodesLogService : public Node
2450{
2451 public:
2452 PostCodesLogService(CrowApp &app) :
2453 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/")
2454 {
2455 entityPrivileges = {
2456 {boost::beast::http::verb::get, {{"Login"}}},
2457 {boost::beast::http::verb::head, {{"Login"}}},
2458 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2459 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2460 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2461 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2462 }
2463
2464 private:
2465 void doGet(crow::Response &res, const crow::Request &req,
2466 const std::vector<std::string> &params) override
2467 {
2468 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2469
2470 asyncResp->res.jsonValue = {
2471 {"@odata.id", "/redfish/v1/Systems/system/LogServices/PostCodes"},
2472 {"@odata.type", "#LogService.v1_1_0.LogService"},
2473 {"@odata.context", "/redfish/v1/$metadata#LogService.LogService"},
2474 {"Name", "POST Code Log Service"},
2475 {"Description", "POST Code Log Service"},
2476 {"Id", "BIOS POST Code Log"},
2477 {"OverWritePolicy", "WrapsWhenFull"},
2478 {"Entries",
2479 {{"@odata.id",
2480 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries"}}}};
2481 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
2482 {"target", "/redfish/v1/Systems/system/LogServices/PostCodes/"
2483 "Actions/LogService.ClearLog"}};
2484 }
2485};
2486
2487class PostCodesClear : public Node
2488{
2489 public:
2490 PostCodesClear(CrowApp &app) :
2491 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Actions/"
2492 "LogService.ClearLog/")
2493 {
2494 entityPrivileges = {
2495 {boost::beast::http::verb::get, {{"Login"}}},
2496 {boost::beast::http::verb::head, {{"Login"}}},
AppaRao Puli39460282020-04-07 17:03:04 +05302497 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
2498 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
2499 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
2500 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
ZhikuiRena3316fc2020-01-29 14:58:08 -08002501 }
2502
2503 private:
2504 void doPost(crow::Response &res, const crow::Request &req,
2505 const std::vector<std::string> &params) override
2506 {
2507 BMCWEB_LOG_DEBUG << "Do delete all postcodes entries.";
2508
2509 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2510 // Make call to post-code service to request clear all
2511 crow::connections::systemBus->async_method_call(
2512 [asyncResp](const boost::system::error_code ec) {
2513 if (ec)
2514 {
2515 // TODO Handle for specific error code
2516 BMCWEB_LOG_ERROR
2517 << "doClearPostCodes resp_handler got error " << ec;
2518 asyncResp->res.result(
2519 boost::beast::http::status::internal_server_error);
2520 messages::internalError(asyncResp->res);
2521 return;
2522 }
2523 },
2524 "xyz.openbmc_project.State.Boot.PostCode",
2525 "/xyz/openbmc_project/State/Boot/PostCode",
2526 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2527 }
2528};
2529
2530static void fillPostCodeEntry(
2531 std::shared_ptr<AsyncResp> aResp,
2532 const boost::container::flat_map<uint64_t, uint64_t> &postcode,
2533 const uint16_t bootIndex, const uint64_t codeIndex = 0,
2534 const uint64_t skip = 0, const uint64_t top = 0)
2535{
2536 // Get the Message from the MessageRegistry
2537 const message_registries::Message *message =
2538 message_registries::getMessage("OpenBMC.0.1.BIOSPOSTCode");
2539 std::string severity;
2540 if (message != nullptr)
2541 {
2542 severity = message->severity;
2543 }
2544
2545 uint64_t currentCodeIndex = 0;
2546 nlohmann::json &logEntryArray = aResp->res.jsonValue["Members"];
2547
2548 uint64_t firstCodeTimeUs = 0;
2549 for (const std::pair<uint64_t, uint64_t> &code : postcode)
2550 {
2551 currentCodeIndex++;
2552 std::string postcodeEntryID =
2553 "B" + std::to_string(bootIndex) + "-" +
2554 std::to_string(currentCodeIndex); // 1 based index in EntryID string
2555
2556 uint64_t usecSinceEpoch = code.first;
2557 uint64_t usTimeOffset = 0;
2558
2559 if (1 == currentCodeIndex)
2560 { // already incremented
2561 firstCodeTimeUs = code.first;
2562 }
2563 else
2564 {
2565 usTimeOffset = code.first - firstCodeTimeUs;
2566 }
2567
2568 // skip if no specific codeIndex is specified and currentCodeIndex does
2569 // not fall between top and skip
2570 if ((codeIndex == 0) &&
2571 (currentCodeIndex <= skip || currentCodeIndex > top))
2572 {
2573 continue;
2574 }
2575
2576 // skip if a sepcific codeIndex is specified and does not match the
2577 // currentIndex
2578 if ((codeIndex > 0) && (currentCodeIndex != codeIndex))
2579 {
2580 // This is done for simplicity. 1st entry is needed to calculate
2581 // time offset. To improve efficiency, one can get to the entry
2582 // directly (possibly with flatmap's nth method)
2583 continue;
2584 }
2585
2586 // currentCodeIndex is within top and skip or equal to specified code
2587 // index
2588
2589 // Get the Created time from the timestamp
2590 std::string entryTimeStr;
2591 if (!getTimestampStr(usecSinceEpoch, entryTimeStr))
2592 {
2593 continue;
2594 }
2595
2596 // assemble messageArgs: BootIndex, TimeOffset(100us), PostCode(hex)
2597 std::ostringstream hexCode;
2598 hexCode << "0x" << std::setfill('0') << std::setw(2) << std::hex
2599 << code.second;
2600 std::ostringstream timeOffsetStr;
2601 // Set Fixed -Point Notation
2602 timeOffsetStr << std::fixed;
2603 // Set precision to 4 digits
2604 timeOffsetStr << std::setprecision(4);
2605 // Add double to stream
2606 timeOffsetStr << static_cast<double>(usTimeOffset) / 1000 / 1000;
2607 std::vector<std::string> messageArgs = {
2608 std::to_string(bootIndex), timeOffsetStr.str(), hexCode.str()};
2609
2610 // Get MessageArgs template from message registry
2611 std::string msg;
2612 if (message != nullptr)
2613 {
2614 msg = message->message;
2615
2616 // fill in this post code value
2617 int i = 0;
2618 for (const std::string &messageArg : messageArgs)
2619 {
2620 std::string argStr = "%" + std::to_string(++i);
2621 size_t argPos = msg.find(argStr);
2622 if (argPos != std::string::npos)
2623 {
2624 msg.replace(argPos, argStr.length(), messageArg);
2625 }
2626 }
2627 }
2628
2629 // add to AsyncResp
2630 logEntryArray.push_back({});
2631 nlohmann::json &bmcLogEntry = logEntryArray.back();
2632 bmcLogEntry = {
2633 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
2634 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
2635 {"@odata.id", "/redfish/v1/Systems/system/LogServices/"
2636 "PostCodes/Entries/" +
2637 postcodeEntryID},
2638 {"Name", "POST Code Log Entry"},
2639 {"Id", postcodeEntryID},
2640 {"Message", std::move(msg)},
2641 {"MessageId", "OpenBMC.0.1.BIOSPOSTCode"},
2642 {"MessageArgs", std::move(messageArgs)},
2643 {"EntryType", "Event"},
2644 {"Severity", std::move(severity)},
2645 {"Created", std::move(entryTimeStr)}};
2646 }
2647}
2648
2649static void getPostCodeForEntry(std::shared_ptr<AsyncResp> aResp,
2650 const uint16_t bootIndex,
2651 const uint64_t codeIndex)
2652{
2653 crow::connections::systemBus->async_method_call(
2654 [aResp, bootIndex, codeIndex](
2655 const boost::system::error_code ec,
2656 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2657 if (ec)
2658 {
2659 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2660 messages::internalError(aResp->res);
2661 return;
2662 }
2663
2664 // skip the empty postcode boots
2665 if (postcode.empty())
2666 {
2667 return;
2668 }
2669
2670 fillPostCodeEntry(aResp, postcode, bootIndex, codeIndex);
2671
2672 aResp->res.jsonValue["Members@odata.count"] =
2673 aResp->res.jsonValue["Members"].size();
2674 },
2675 "xyz.openbmc_project.State.Boot.PostCode",
2676 "/xyz/openbmc_project/State/Boot/PostCode",
2677 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2678 bootIndex);
2679}
2680
2681static void getPostCodeForBoot(std::shared_ptr<AsyncResp> aResp,
2682 const uint16_t bootIndex,
2683 const uint16_t bootCount,
2684 const uint64_t entryCount, const uint64_t skip,
2685 const uint64_t top)
2686{
2687 crow::connections::systemBus->async_method_call(
2688 [aResp, bootIndex, bootCount, entryCount, skip,
2689 top](const boost::system::error_code ec,
2690 const boost::container::flat_map<uint64_t, uint64_t> &postcode) {
2691 if (ec)
2692 {
2693 BMCWEB_LOG_DEBUG << "DBUS POST CODE PostCode response error";
2694 messages::internalError(aResp->res);
2695 return;
2696 }
2697
2698 uint64_t endCount = entryCount;
2699 if (!postcode.empty())
2700 {
2701 endCount = entryCount + postcode.size();
2702
2703 if ((skip < endCount) && ((top + skip) > entryCount))
2704 {
2705 uint64_t thisBootSkip =
2706 std::max(skip, entryCount) - entryCount;
2707 uint64_t thisBootTop =
2708 std::min(top + skip, endCount) - entryCount;
2709
2710 fillPostCodeEntry(aResp, postcode, bootIndex, 0,
2711 thisBootSkip, thisBootTop);
2712 }
2713 aResp->res.jsonValue["Members@odata.count"] = endCount;
2714 }
2715
2716 // continue to previous bootIndex
2717 if (bootIndex < bootCount)
2718 {
2719 getPostCodeForBoot(aResp, static_cast<uint16_t>(bootIndex + 1),
2720 bootCount, endCount, skip, top);
2721 }
2722 else
2723 {
2724 aResp->res.jsonValue["Members@odata.nextLink"] =
2725 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2726 "Entries?$skip=" +
2727 std::to_string(skip + top);
2728 }
2729 },
2730 "xyz.openbmc_project.State.Boot.PostCode",
2731 "/xyz/openbmc_project/State/Boot/PostCode",
2732 "xyz.openbmc_project.State.Boot.PostCode", "GetPostCodesWithTimeStamp",
2733 bootIndex);
2734}
2735
2736static void getCurrentBootNumber(std::shared_ptr<AsyncResp> aResp,
2737 const uint64_t skip, const uint64_t top)
2738{
2739 uint64_t entryCount = 0;
2740 crow::connections::systemBus->async_method_call(
2741 [aResp, entryCount, skip,
2742 top](const boost::system::error_code ec,
2743 const std::variant<uint16_t> &bootCount) {
2744 if (ec)
2745 {
2746 BMCWEB_LOG_DEBUG << "DBUS response error " << ec;
2747 messages::internalError(aResp->res);
2748 return;
2749 }
2750 auto pVal = std::get_if<uint16_t>(&bootCount);
2751 if (pVal)
2752 {
2753 getPostCodeForBoot(aResp, 1, *pVal, entryCount, skip, top);
2754 }
2755 else
2756 {
2757 BMCWEB_LOG_DEBUG << "Post code boot index failed.";
2758 }
2759 },
2760 "xyz.openbmc_project.State.Boot.PostCode",
2761 "/xyz/openbmc_project/State/Boot/PostCode",
2762 "org.freedesktop.DBus.Properties", "Get",
2763 "xyz.openbmc_project.State.Boot.PostCode", "CurrentBootCycleCount");
2764}
2765
2766class PostCodesEntryCollection : public Node
2767{
2768 public:
2769 template <typename CrowApp>
2770 PostCodesEntryCollection(CrowApp &app) :
2771 Node(app, "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/")
2772 {
2773 entityPrivileges = {
2774 {boost::beast::http::verb::get, {{"Login"}}},
2775 {boost::beast::http::verb::head, {{"Login"}}},
2776 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2777 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2778 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2779 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2780 }
2781
2782 private:
2783 void doGet(crow::Response &res, const crow::Request &req,
2784 const std::vector<std::string> &params) override
2785 {
2786 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2787
2788 asyncResp->res.jsonValue["@odata.type"] =
2789 "#LogEntryCollection.LogEntryCollection";
2790 asyncResp->res.jsonValue["@odata.context"] =
2791 "/redfish/v1/"
2792 "$metadata#LogEntryCollection.LogEntryCollection";
2793 asyncResp->res.jsonValue["@odata.id"] =
2794 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries";
2795 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2796 asyncResp->res.jsonValue["Description"] =
2797 "Collection of POST Code Log Entries";
2798 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2799 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2800
2801 uint64_t skip = 0;
2802 uint64_t top = maxEntriesPerPage; // Show max entries by default
2803 if (!getSkipParam(asyncResp->res, req, skip))
2804 {
2805 return;
2806 }
2807 if (!getTopParam(asyncResp->res, req, top))
2808 {
2809 return;
2810 }
2811 getCurrentBootNumber(asyncResp, skip, top);
2812 }
2813};
2814
2815class PostCodesEntry : public Node
2816{
2817 public:
2818 PostCodesEntry(CrowApp &app) :
2819 Node(app,
2820 "/redfish/v1/Systems/system/LogServices/PostCodes/Entries/<str>/",
2821 std::string())
2822 {
2823 entityPrivileges = {
2824 {boost::beast::http::verb::get, {{"Login"}}},
2825 {boost::beast::http::verb::head, {{"Login"}}},
2826 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2827 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2828 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2829 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2830 }
2831
2832 private:
2833 void doGet(crow::Response &res, const crow::Request &req,
2834 const std::vector<std::string> &params) override
2835 {
2836 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
2837 if (params.size() != 1)
2838 {
2839 messages::internalError(asyncResp->res);
2840 return;
2841 }
2842
2843 const std::string &targetID = params[0];
2844
2845 size_t bootPos = targetID.find('B');
2846 if (bootPos == std::string::npos)
2847 {
2848 // Requested ID was not found
2849 messages::resourceMissingAtURI(asyncResp->res, targetID);
2850 return;
2851 }
2852 std::string_view bootIndexStr(targetID);
2853 bootIndexStr.remove_prefix(bootPos + 1);
2854 uint16_t bootIndex = 0;
2855 uint64_t codeIndex = 0;
2856 size_t dashPos = bootIndexStr.find('-');
2857
2858 if (dashPos == std::string::npos)
2859 {
2860 return;
2861 }
2862 std::string_view codeIndexStr(bootIndexStr);
2863 bootIndexStr.remove_suffix(dashPos);
2864 codeIndexStr.remove_prefix(dashPos + 1);
2865
2866 bootIndex = static_cast<uint16_t>(
2867 strtoul(std::string(bootIndexStr).c_str(), NULL, 0));
2868 codeIndex = strtoul(std::string(codeIndexStr).c_str(), NULL, 0);
2869 if (bootIndex == 0 || codeIndex == 0)
2870 {
2871 BMCWEB_LOG_DEBUG << "Get Post Code invalid entry string "
2872 << params[0];
2873 }
2874
2875 asyncResp->res.jsonValue["@odata.type"] = "#LogEntry.v1_4_0.LogEntry";
2876 asyncResp->res.jsonValue["@odata.context"] =
2877 "/redfish/v1/$metadata#LogEntry.LogEntry";
2878 asyncResp->res.jsonValue["@odata.id"] =
2879 "/redfish/v1/Systems/system/LogServices/PostCodes/"
2880 "Entries";
2881 asyncResp->res.jsonValue["Name"] = "BIOS POST Code Log Entries";
2882 asyncResp->res.jsonValue["Description"] =
2883 "Collection of POST Code Log Entries";
2884 asyncResp->res.jsonValue["Members"] = nlohmann::json::array();
2885 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2886
2887 getPostCodeForEntry(asyncResp, bootIndex, codeIndex);
2888 }
2889};
2890
Ed Tanous1da66f72018-07-27 16:13:37 -07002891} // namespace redfish