blob: ca9604b63db0e2a8e46e1595b6b6c75cbcbd377a [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<
99 std::string,
100 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
101 int32_t, uint32_t, int64_t, uint64_t, double>>;
102
103using GetManagedObjectsType = boost::container::flat_map<
104 sdbusplus::message::object_path,
105 boost::container::flat_map<std::string, GetManagedPropertyType>>;
106
107inline std::string translateSeverityDbusToRedfish(const std::string &s)
108{
109 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
110 {
111 return "Critical";
112 }
113 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
114 {
115 return "Critical";
116 }
117 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
118 {
119 return "OK";
120 }
121 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
122 {
123 return "Critical";
124 }
125 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
126 {
127 return "Critical";
128 }
129 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
130 {
131 return "OK";
132 }
133 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
134 {
135 return "OK";
136 }
137 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
138 {
139 return "Warning";
140 }
141 return "";
142}
143
Jason M. Bills16428a12018-11-02 12:42:29 -0700144static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800145 const std::string_view &field,
146 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700147{
148 const char *data = nullptr;
149 size_t length = 0;
150 int ret = 0;
151 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700152 ret = sd_journal_get_data(journal, field.data(),
153 reinterpret_cast<const void **>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700154 if (ret < 0)
155 {
156 return ret;
157 }
Ed Tanous39e77502019-03-04 17:35:53 -0800158 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700159 // Only use the content after the "=" character.
160 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
161 return ret;
162}
163
164static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800165 const std::string_view &field, const int &base,
Ed Tanous271584a2019-07-09 16:24:22 -0700166 long int &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700167{
168 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800169 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700170 // Get the metadata from the requested field of the journal entry
171 ret = getJournalMetadata(journal, field, metadata);
172 if (ret < 0)
173 {
174 return ret;
175 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000176 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700177 return ret;
178}
179
180static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
181{
182 int ret = 0;
183 uint64_t timestamp = 0;
184 ret = sd_journal_get_realtime_usec(journal, &timestamp);
185 if (ret < 0)
186 {
187 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
188 << strerror(-ret);
189 return false;
190 }
191 time_t t =
192 static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
193 struct tm *loctime = localtime(&t);
194 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700195 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700196 {
197 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
198 }
199 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800200 std::string_view t1(entryTime);
201 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700202 if (t1.size() > 2 && t2.size() > 2)
203 {
204 t1.remove_suffix(2);
205 t2.remove_prefix(t2.size() - 2);
206 }
Ed Tanous39e77502019-03-04 17:35:53 -0800207 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700208 return true;
209}
210
211static bool getSkipParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700212 uint64_t &skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700213{
214 char *skipParam = req.urlParams.get("$skip");
215 if (skipParam != nullptr)
216 {
217 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700218 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700219 if (*skipParam == '\0' || *ptr != '\0')
220 {
221
222 messages::queryParameterValueTypeError(res, std::string(skipParam),
223 "$skip");
224 return false;
225 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700226 }
227 return true;
228}
229
Ed Tanous271584a2019-07-09 16:24:22 -0700230static constexpr const uint64_t maxEntriesPerPage = 1000;
Jason M. Bills16428a12018-11-02 12:42:29 -0700231static bool getTopParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700232 uint64_t &top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700233{
234 char *topParam = req.urlParams.get("$top");
235 if (topParam != nullptr)
236 {
237 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700238 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700239 if (*topParam == '\0' || *ptr != '\0')
240 {
241 messages::queryParameterValueTypeError(res, std::string(topParam),
242 "$top");
243 return false;
244 }
Ed Tanous271584a2019-07-09 16:24:22 -0700245 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700246 {
247
248 messages::queryParameterOutOfRange(
249 res, std::to_string(top), "$top",
250 "1-" + std::to_string(maxEntriesPerPage));
251 return false;
252 }
253 }
254 return true;
255}
256
Jason M. Billse85d6b12019-07-29 17:01:15 -0700257static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
258 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700259{
260 int ret = 0;
261 static uint64_t prevTs = 0;
262 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700263 if (firstEntry)
264 {
265 prevTs = 0;
266 }
267
Jason M. Bills16428a12018-11-02 12:42:29 -0700268 // Get the entry timestamp
269 uint64_t curTs = 0;
270 ret = sd_journal_get_realtime_usec(journal, &curTs);
271 if (ret < 0)
272 {
273 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
274 << strerror(-ret);
275 return false;
276 }
277 // If the timestamp isn't unique, increment the index
278 if (curTs == prevTs)
279 {
280 index++;
281 }
282 else
283 {
284 // Otherwise, reset it
285 index = 0;
286 }
287 // Save the timestamp
288 prevTs = curTs;
289
290 entryID = std::to_string(curTs);
291 if (index > 0)
292 {
293 entryID += "_" + std::to_string(index);
294 }
295 return true;
296}
297
Jason M. Billse85d6b12019-07-29 17:01:15 -0700298static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
299 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700300{
Ed Tanous271584a2019-07-09 16:24:22 -0700301 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700302 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700303 if (firstEntry)
304 {
305 prevTs = 0;
306 }
307
Jason M. Bills95820182019-04-22 16:25:34 -0700308 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700309 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700310 std::tm timeStruct = {};
311 std::istringstream entryStream(logEntry);
312 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
313 {
314 curTs = std::mktime(&timeStruct);
315 }
316 // If the timestamp isn't unique, increment the index
317 if (curTs == prevTs)
318 {
319 index++;
320 }
321 else
322 {
323 // Otherwise, reset it
324 index = 0;
325 }
326 // Save the timestamp
327 prevTs = curTs;
328
329 entryID = std::to_string(curTs);
330 if (index > 0)
331 {
332 entryID += "_" + std::to_string(index);
333 }
334 return true;
335}
336
Jason M. Bills16428a12018-11-02 12:42:29 -0700337static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
Ed Tanous271584a2019-07-09 16:24:22 -0700338 uint64_t &timestamp, uint64_t &index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700339{
340 if (entryID.empty())
341 {
342 return false;
343 }
344 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800345 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700346
347 auto underscorePos = tsStr.find("_");
348 if (underscorePos != tsStr.npos)
349 {
350 // Timestamp has an index
351 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800352 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700353 indexStr.remove_prefix(underscorePos + 1);
354 std::size_t pos;
355 try
356 {
Ed Tanous39e77502019-03-04 17:35:53 -0800357 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700358 }
Ed Tanous271584a2019-07-09 16:24:22 -0700359 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700360 {
361 messages::resourceMissingAtURI(res, entryID);
362 return false;
363 }
Ed Tanous271584a2019-07-09 16:24:22 -0700364 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700365 {
366 messages::resourceMissingAtURI(res, entryID);
367 return false;
368 }
369 if (pos != indexStr.size())
370 {
371 messages::resourceMissingAtURI(res, entryID);
372 return false;
373 }
374 }
375 // Timestamp has no index
376 std::size_t pos;
377 try
378 {
Ed Tanous39e77502019-03-04 17:35:53 -0800379 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700380 }
Ed Tanous271584a2019-07-09 16:24:22 -0700381 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700382 {
383 messages::resourceMissingAtURI(res, entryID);
384 return false;
385 }
Ed Tanous271584a2019-07-09 16:24:22 -0700386 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700387 {
388 messages::resourceMissingAtURI(res, entryID);
389 return false;
390 }
391 if (pos != tsStr.size())
392 {
393 messages::resourceMissingAtURI(res, entryID);
394 return false;
395 }
396 return true;
397}
398
Jason M. Bills95820182019-04-22 16:25:34 -0700399static bool
400 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
401{
402 static const std::filesystem::path redfishLogDir = "/var/log";
403 static const std::string redfishLogFilename = "redfish";
404
405 // Loop through the directory looking for redfish log files
406 for (const std::filesystem::directory_entry &dirEnt :
407 std::filesystem::directory_iterator(redfishLogDir))
408 {
409 // If we find a redfish log file, save the path
410 std::string filename = dirEnt.path().filename();
411 if (boost::starts_with(filename, redfishLogFilename))
412 {
413 redfishLogFiles.emplace_back(redfishLogDir / filename);
414 }
415 }
416 // As the log files rotate, they are appended with a ".#" that is higher for
417 // the older logs. Since we don't expect more than 10 log files, we
418 // can just sort the list to get them in order from newest to oldest
419 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
420
421 return !redfishLogFiles.empty();
422}
423
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800424class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700425{
426 public:
427 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800428 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800429 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800430 {
431 entityPrivileges = {
432 {boost::beast::http::verb::get, {{"Login"}}},
433 {boost::beast::http::verb::head, {{"Login"}}},
434 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
435 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
436 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
437 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
438 }
439
440 private:
441 /**
442 * Functions triggers appropriate requests on DBus
443 */
444 void doGet(crow::Response &res, const crow::Request &req,
445 const std::vector<std::string> &params) override
446 {
447 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800448 // Collections don't include the static data added by SubRoute because
449 // it has a duplicate entry for members
450 asyncResp->res.jsonValue["@odata.type"] =
451 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800452 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800453 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800454 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
455 asyncResp->res.jsonValue["Description"] =
456 "Collection of LogServices for this Computer System";
457 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
458 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800459 logServiceArray.push_back(
460 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800461#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
462 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500463 {{"@odata.id",
464 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800465#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800466 asyncResp->res.jsonValue["Members@odata.count"] =
467 logServiceArray.size();
468 }
469};
470
471class EventLogService : public Node
472{
473 public:
474 template <typename CrowApp>
475 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800476 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800477 {
478 entityPrivileges = {
479 {boost::beast::http::verb::get, {{"Login"}}},
480 {boost::beast::http::verb::head, {{"Login"}}},
481 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
482 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
483 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
484 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
485 }
486
487 private:
488 void doGet(crow::Response &res, const crow::Request &req,
489 const std::vector<std::string> &params) override
490 {
491 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
492
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800493 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800494 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800495 asyncResp->res.jsonValue["@odata.type"] =
496 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800497 asyncResp->res.jsonValue["Name"] = "Event Log Service";
498 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
499 asyncResp->res.jsonValue["Id"] = "Event Log";
500 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
501 asyncResp->res.jsonValue["Entries"] = {
502 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800503 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500504 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
505
506 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
507 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700508 }
509};
510
Tim Lee1f56a3a2019-10-09 10:17:57 +0800511class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700512{
513 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800514 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700515 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
516 "LogService.ClearLog/")
517 {
518 entityPrivileges = {
519 {boost::beast::http::verb::get, {{"Login"}}},
520 {boost::beast::http::verb::head, {{"Login"}}},
521 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
522 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
523 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
524 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
525 }
526
527 private:
528 void doPost(crow::Response &res, const crow::Request &req,
529 const std::vector<std::string> &params) override
530 {
531 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
532
533 // Clear the EventLog by deleting the log files
534 std::vector<std::filesystem::path> redfishLogFiles;
535 if (getRedfishLogFiles(redfishLogFiles))
536 {
537 for (const std::filesystem::path &file : redfishLogFiles)
538 {
539 std::error_code ec;
540 std::filesystem::remove(file, ec);
541 }
542 }
543
544 // Reload rsyslog so it knows to start new log files
545 crow::connections::systemBus->async_method_call(
546 [asyncResp](const boost::system::error_code ec) {
547 if (ec)
548 {
549 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
550 messages::internalError(asyncResp->res);
551 return;
552 }
553
554 messages::success(asyncResp->res);
555 },
556 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
557 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
558 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800559 }
560};
561
Jason M. Bills95820182019-04-22 16:25:34 -0700562static int fillEventLogEntryJson(const std::string &logEntryID,
563 const std::string logEntry,
564 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800565{
Jason M. Bills95820182019-04-22 16:25:34 -0700566 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700567 // First get the Timestamp
568 size_t space = logEntry.find_first_of(" ");
569 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700570 {
571 return 1;
572 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700573 std::string timestamp = logEntry.substr(0, space);
574 // Then get the log contents
575 size_t entryStart = logEntry.find_first_not_of(" ", space);
576 if (entryStart == std::string::npos)
577 {
578 return 1;
579 }
580 std::string_view entry(logEntry);
581 entry.remove_prefix(entryStart);
582 // Use split to separate the entry into its fields
583 std::vector<std::string> logEntryFields;
584 boost::split(logEntryFields, entry, boost::is_any_of(","),
585 boost::token_compress_on);
586 // We need at least a MessageId to be valid
587 if (logEntryFields.size() < 1)
588 {
589 return 1;
590 }
591 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700592
Jason M. Bills4851d452019-03-28 11:27:48 -0700593 // Get the Message from the MessageRegistry
594 const message_registries::Message *message =
595 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800596
Jason M. Bills4851d452019-03-28 11:27:48 -0700597 std::string msg;
598 std::string severity;
599 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800600 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700601 msg = message->message;
602 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800603 }
604
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700605 // Get the MessageArgs from the log if there are any
606 boost::beast::span<std::string> messageArgs;
607 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700608 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700609 std::string &messageArgsStart = logEntryFields[1];
610 // If the first string is empty, assume there are no MessageArgs
611 std::size_t messageArgsSize = 0;
612 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700613 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700614 messageArgsSize = logEntryFields.size() - 1;
615 }
616
617 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
618
619 // Fill the MessageArgs into the Message
620 int i = 0;
621 for (const std::string &messageArg : messageArgs)
622 {
623 std::string argStr = "%" + std::to_string(++i);
624 size_t argPos = msg.find(argStr);
625 if (argPos != std::string::npos)
626 {
627 msg.replace(argPos, argStr.length(), messageArg);
628 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700629 }
630 }
631
Jason M. Bills95820182019-04-22 16:25:34 -0700632 // Get the Created time from the timestamp. The log timestamp is in RFC3339
633 // format which matches the Redfish format except for the fractional seconds
634 // between the '.' and the '+', so just remove them.
635 std::size_t dot = timestamp.find_first_of(".");
636 std::size_t plus = timestamp.find_first_of("+");
637 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800638 {
Jason M. Bills95820182019-04-22 16:25:34 -0700639 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800640 }
641
642 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700643 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700644 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800645 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700646 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700647 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800648 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700649 {"Id", logEntryID},
650 {"Message", std::move(msg)},
651 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800652 {"MessageArgs", std::move(messageArgs)},
653 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700654 {"Severity", std::move(severity)},
655 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800656 return 0;
657}
658
Anthony Wilson27062602019-04-22 02:10:09 -0500659class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800660{
661 public:
662 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500663 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800664 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800665 {
666 entityPrivileges = {
667 {boost::beast::http::verb::get, {{"Login"}}},
668 {boost::beast::http::verb::head, {{"Login"}}},
669 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
670 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
671 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
672 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
673 }
674
675 private:
676 void doGet(crow::Response &res, const crow::Request &req,
677 const std::vector<std::string> &params) override
678 {
679 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700680 uint64_t skip = 0;
681 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800682 if (!getSkipParam(asyncResp->res, req, skip))
683 {
684 return;
685 }
686 if (!getTopParam(asyncResp->res, req, top))
687 {
688 return;
689 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800690 // Collections don't include the static data added by SubRoute because
691 // it has a duplicate entry for members
692 asyncResp->res.jsonValue["@odata.type"] =
693 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800694 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800695 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800696 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
697 asyncResp->res.jsonValue["Description"] =
698 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700699
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800700 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
701 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700702 // Go through the log files and create a unique ID for each entry
703 std::vector<std::filesystem::path> redfishLogFiles;
704 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000705 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700706 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700707
708 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700709 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
710 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800711 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700712 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700713 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800714 {
715 continue;
716 }
717
Jason M. Billse85d6b12019-07-29 17:01:15 -0700718 // Reset the unique ID on the first entry
719 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700720 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800721 {
Jason M. Bills95820182019-04-22 16:25:34 -0700722 entryCount++;
723 // Handle paging using skip (number of entries to skip from the
724 // start) and top (number of entries to display)
725 if (entryCount <= skip || entryCount > skip + top)
726 {
727 continue;
728 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800729
Jason M. Bills95820182019-04-22 16:25:34 -0700730 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700731 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700732 {
733 continue;
734 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800735
Jason M. Billse85d6b12019-07-29 17:01:15 -0700736 if (firstEntry)
737 {
738 firstEntry = false;
739 }
740
Jason M. Bills95820182019-04-22 16:25:34 -0700741 logEntryArray.push_back({});
742 nlohmann::json &bmcLogEntry = logEntryArray.back();
743 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
744 {
745 messages::internalError(asyncResp->res);
746 return;
747 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800748 }
749 }
750 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
751 if (skip + top < entryCount)
752 {
753 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700754 "/redfish/v1/Systems/system/LogServices/EventLog/"
755 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800756 std::to_string(skip + top);
757 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500758 }
759};
760
Jason M. Bills897967d2019-07-29 17:05:30 -0700761class JournalEventLogEntry : public Node
762{
763 public:
764 JournalEventLogEntry(CrowApp &app) :
765 Node(app,
766 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
767 std::string())
768 {
769 entityPrivileges = {
770 {boost::beast::http::verb::get, {{"Login"}}},
771 {boost::beast::http::verb::head, {{"Login"}}},
772 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
773 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
774 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
775 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
776 }
777
778 private:
779 void doGet(crow::Response &res, const crow::Request &req,
780 const std::vector<std::string> &params) override
781 {
782 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
783 if (params.size() != 1)
784 {
785 messages::internalError(asyncResp->res);
786 return;
787 }
788 const std::string &targetID = params[0];
789
790 // Go through the log files and check the unique ID for each entry to
791 // find the target entry
792 std::vector<std::filesystem::path> redfishLogFiles;
793 getRedfishLogFiles(redfishLogFiles);
794 std::string logEntry;
795
796 // Oldest logs are in the last file, so start there and loop backwards
797 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
798 it++)
799 {
800 std::ifstream logStream(*it);
801 if (!logStream.is_open())
802 {
803 continue;
804 }
805
806 // Reset the unique ID on the first entry
807 bool firstEntry = true;
808 while (std::getline(logStream, logEntry))
809 {
810 std::string idStr;
811 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
812 {
813 continue;
814 }
815
816 if (firstEntry)
817 {
818 firstEntry = false;
819 }
820
821 if (idStr == targetID)
822 {
823 if (fillEventLogEntryJson(idStr, logEntry,
824 asyncResp->res.jsonValue) != 0)
825 {
826 messages::internalError(asyncResp->res);
827 return;
828 }
829 return;
830 }
831 }
832 }
833 // Requested ID was not found
834 messages::resourceMissingAtURI(asyncResp->res, targetID);
835 }
836};
837
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500838class DBusEventLogEntryCollection : public Node
839{
840 public:
841 template <typename CrowApp>
842 DBusEventLogEntryCollection(CrowApp &app) :
843 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
844 {
845 entityPrivileges = {
846 {boost::beast::http::verb::get, {{"Login"}}},
847 {boost::beast::http::verb::head, {{"Login"}}},
848 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
849 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
850 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
851 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
852 }
853
854 private:
855 void doGet(crow::Response &res, const crow::Request &req,
856 const std::vector<std::string> &params) override
857 {
858 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
859
860 // Collections don't include the static data added by SubRoute because
861 // it has a duplicate entry for members
862 asyncResp->res.jsonValue["@odata.type"] =
863 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500864 asyncResp->res.jsonValue["@odata.id"] =
865 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
866 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
867 asyncResp->res.jsonValue["Description"] =
868 "Collection of System Event Log Entries";
869
Andrew Geisslercb92c032018-08-17 07:56:14 -0700870 // DBus implementation of EventLog/Entries
871 // Make call to Logging Service to find all log entry objects
872 crow::connections::systemBus->async_method_call(
873 [asyncResp](const boost::system::error_code ec,
874 GetManagedObjectsType &resp) {
875 if (ec)
876 {
877 // TODO Handle for specific error code
878 BMCWEB_LOG_ERROR
879 << "getLogEntriesIfaceData resp_handler got error "
880 << ec;
881 messages::internalError(asyncResp->res);
882 return;
883 }
884 nlohmann::json &entriesArray =
885 asyncResp->res.jsonValue["Members"];
886 entriesArray = nlohmann::json::array();
887 for (auto &objectPath : resp)
888 {
889 for (auto &interfaceMap : objectPath.second)
890 {
891 if (interfaceMap.first !=
892 "xyz.openbmc_project.Logging.Entry")
893 {
894 BMCWEB_LOG_DEBUG << "Bailing early on "
895 << interfaceMap.first;
896 continue;
897 }
898 entriesArray.push_back({});
899 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -0700900 uint32_t *id = nullptr;
901 std::time_t timestamp{};
902 std::string *severity = nullptr;
903 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700904 for (auto &propertyMap : interfaceMap.second)
905 {
906 if (propertyMap.first == "Id")
907 {
908 id = sdbusplus::message::variant_ns::get_if<
909 uint32_t>(&propertyMap.second);
910 if (id == nullptr)
911 {
912 messages::propertyMissing(asyncResp->res,
913 "Id");
914 }
915 }
916 else if (propertyMap.first == "Timestamp")
917 {
918 const uint64_t *millisTimeStamp =
919 std::get_if<uint64_t>(&propertyMap.second);
920 if (millisTimeStamp == nullptr)
921 {
922 messages::propertyMissing(asyncResp->res,
923 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -0700924 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700925 }
926 // Retrieve Created property with format:
927 // yyyy-mm-ddThh:mm:ss
928 std::chrono::milliseconds chronoTimeStamp(
929 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -0700930 timestamp = std::chrono::duration_cast<
931 std::chrono::duration<int>>(
932 chronoTimeStamp)
933 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -0700934 }
935 else if (propertyMap.first == "Severity")
936 {
937 severity = std::get_if<std::string>(
938 &propertyMap.second);
939 if (severity == nullptr)
940 {
941 messages::propertyMissing(asyncResp->res,
942 "Severity");
943 }
944 }
945 else if (propertyMap.first == "Message")
946 {
947 message = std::get_if<std::string>(
948 &propertyMap.second);
949 if (message == nullptr)
950 {
951 messages::propertyMissing(asyncResp->res,
952 "Message");
953 }
954 }
955 }
956 thisEntry = {
957 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700958 {"@odata.id",
959 "/redfish/v1/Systems/system/LogServices/EventLog/"
960 "Entries/" +
961 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -0500962 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700963 {"Id", std::to_string(*id)},
964 {"Message", *message},
965 {"EntryType", "Event"},
966 {"Severity",
967 translateSeverityDbusToRedfish(*severity)},
968 {"Created", crow::utility::getDateTime(timestamp)}};
969 }
970 }
971 std::sort(entriesArray.begin(), entriesArray.end(),
972 [](const nlohmann::json &left,
973 const nlohmann::json &right) {
974 return (left["Id"] <= right["Id"]);
975 });
976 asyncResp->res.jsonValue["Members@odata.count"] =
977 entriesArray.size();
978 },
979 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
980 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800981 }
982};
983
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500984class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800985{
986 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500987 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800988 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -0800989 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
990 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800991 {
992 entityPrivileges = {
993 {boost::beast::http::verb::get, {{"Login"}}},
994 {boost::beast::http::verb::head, {{"Login"}}},
995 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
996 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
997 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
998 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
999 }
1000
1001 private:
1002 void doGet(crow::Response &res, const crow::Request &req,
1003 const std::vector<std::string> &params) override
1004 {
1005 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001006 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001007 {
1008 messages::internalError(asyncResp->res);
1009 return;
1010 }
Ed Tanous029573d2019-02-01 10:57:49 -08001011 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001012
Andrew Geisslercb92c032018-08-17 07:56:14 -07001013 // DBus implementation of EventLog/Entries
1014 // Make call to Logging Service to find all log entry objects
1015 crow::connections::systemBus->async_method_call(
1016 [asyncResp, entryID](const boost::system::error_code ec,
1017 GetManagedPropertyType &resp) {
1018 if (ec)
1019 {
1020 BMCWEB_LOG_ERROR
1021 << "EventLogEntry (DBus) resp_handler got error " << ec;
1022 messages::internalError(asyncResp->res);
1023 return;
1024 }
Ed Tanous66664f22019-10-11 13:05:49 -07001025 uint32_t *id = nullptr;
1026 std::time_t timestamp{};
1027 std::string *severity = nullptr;
1028 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001029 for (auto &propertyMap : resp)
1030 {
1031 if (propertyMap.first == "Id")
1032 {
1033 id = std::get_if<uint32_t>(&propertyMap.second);
1034 if (id == nullptr)
1035 {
1036 messages::propertyMissing(asyncResp->res, "Id");
1037 }
1038 }
1039 else if (propertyMap.first == "Timestamp")
1040 {
1041 const uint64_t *millisTimeStamp =
1042 std::get_if<uint64_t>(&propertyMap.second);
1043 if (millisTimeStamp == nullptr)
1044 {
1045 messages::propertyMissing(asyncResp->res,
1046 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001047 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001048 }
1049 // Retrieve Created property with format:
1050 // yyyy-mm-ddThh:mm:ss
1051 std::chrono::milliseconds chronoTimeStamp(
1052 *millisTimeStamp);
1053 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001054 std::chrono::duration_cast<
1055 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001056 .count();
1057 }
1058 else if (propertyMap.first == "Severity")
1059 {
1060 severity =
1061 std::get_if<std::string>(&propertyMap.second);
1062 if (severity == nullptr)
1063 {
1064 messages::propertyMissing(asyncResp->res,
1065 "Severity");
1066 }
1067 }
1068 else if (propertyMap.first == "Message")
1069 {
1070 message = std::get_if<std::string>(&propertyMap.second);
1071 if (message == nullptr)
1072 {
1073 messages::propertyMissing(asyncResp->res,
1074 "Message");
1075 }
1076 }
1077 }
Ed Tanous271584a2019-07-09 16:24:22 -07001078 if (id == nullptr || message == nullptr || severity == nullptr)
1079 {
1080 return;
1081 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001082 asyncResp->res.jsonValue = {
1083 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001084 {"@odata.id",
1085 "/redfish/v1/Systems/system/LogServices/EventLog/"
1086 "Entries/" +
1087 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001088 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001089 {"Id", std::to_string(*id)},
1090 {"Message", *message},
1091 {"EntryType", "Event"},
1092 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001093 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001094 },
1095 "xyz.openbmc_project.Logging",
1096 "/xyz/openbmc_project/logging/entry/" + entryID,
1097 "org.freedesktop.DBus.Properties", "GetAll",
1098 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001099 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001100
1101 void doDelete(crow::Response &res, const crow::Request &req,
1102 const std::vector<std::string> &params) override
1103 {
1104
1105 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1106
1107 auto asyncResp = std::make_shared<AsyncResp>(res);
1108
1109 if (params.size() != 1)
1110 {
1111 messages::internalError(asyncResp->res);
1112 return;
1113 }
1114 std::string entryID = params[0];
1115
1116 dbus::utility::escapePathForDbus(entryID);
1117
1118 // Process response from Logging service.
1119 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1120 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1121 if (ec)
1122 {
1123 // TODO Handle for specific error code
1124 BMCWEB_LOG_ERROR
1125 << "EventLogEntry (DBus) doDelete respHandler got error "
1126 << ec;
1127 asyncResp->res.result(
1128 boost::beast::http::status::internal_server_error);
1129 return;
1130 }
1131
1132 asyncResp->res.result(boost::beast::http::status::ok);
1133 };
1134
1135 // Make call to Logging service to request Delete Log
1136 crow::connections::systemBus->async_method_call(
1137 respHandler, "xyz.openbmc_project.Logging",
1138 "/xyz/openbmc_project/logging/entry/" + entryID,
1139 "xyz.openbmc_project.Object.Delete", "Delete");
1140 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001141};
1142
1143class BMCLogServiceCollection : public Node
1144{
1145 public:
1146 template <typename CrowApp>
1147 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001148 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001149 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001150 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001151 {boost::beast::http::verb::get, {{"Login"}}},
1152 {boost::beast::http::verb::head, {{"Login"}}},
1153 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1154 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1155 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1156 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001157 }
1158
1159 private:
1160 /**
1161 * Functions triggers appropriate requests on DBus
1162 */
1163 void doGet(crow::Response &res, const crow::Request &req,
1164 const std::vector<std::string> &params) override
1165 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001166 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001167 // Collections don't include the static data added by SubRoute because
1168 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001169 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001170 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001171 asyncResp->res.jsonValue["@odata.id"] =
1172 "/redfish/v1/Managers/bmc/LogServices";
1173 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1174 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001175 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001176 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1177 logServiceArray = nlohmann::json::array();
1178#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1179 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001180 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001181#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001182 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001183 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001184 }
1185};
1186
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001187class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001188{
1189 public:
1190 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001191 BMCJournalLogService(CrowApp &app) :
1192 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001193 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001194 entityPrivileges = {
1195 {boost::beast::http::verb::get, {{"Login"}}},
1196 {boost::beast::http::verb::head, {{"Login"}}},
1197 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1198 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1199 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1200 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1201 }
1202
1203 private:
1204 void doGet(crow::Response &res, const crow::Request &req,
1205 const std::vector<std::string> &params) override
1206 {
1207 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001208 asyncResp->res.jsonValue["@odata.type"] =
1209 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001210 asyncResp->res.jsonValue["@odata.id"] =
1211 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001212 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1213 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1214 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001215 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001216 asyncResp->res.jsonValue["Entries"] = {
1217 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001218 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001219 }
1220};
1221
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001222static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1223 sd_journal *journal,
1224 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001225{
1226 // Get the Log Entry contents
1227 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001228
Ed Tanous39e77502019-03-04 17:35:53 -08001229 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001230 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001231 if (ret < 0)
1232 {
1233 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1234 return 1;
1235 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001236
1237 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001238 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001239 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001240 if (ret < 0)
1241 {
1242 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001243 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001244
1245 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001246 std::string entryTimeStr;
1247 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001248 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001249 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001250 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001251
1252 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001253 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001254 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001255 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1256 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001257 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001258 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001259 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001260 {"EntryType", "Oem"},
1261 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001262 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001263 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001264 {"Created", std::move(entryTimeStr)}};
1265 return 0;
1266}
1267
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001268class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001269{
1270 public:
1271 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001272 BMCJournalLogEntryCollection(CrowApp &app) :
1273 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001274 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001275 entityPrivileges = {
1276 {boost::beast::http::verb::get, {{"Login"}}},
1277 {boost::beast::http::verb::head, {{"Login"}}},
1278 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1279 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1280 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1281 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1282 }
1283
1284 private:
1285 void doGet(crow::Response &res, const crow::Request &req,
1286 const std::vector<std::string> &params) override
1287 {
1288 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001289 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001290 uint64_t skip = 0;
1291 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001292 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001293 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001294 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001295 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001296 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001297 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001298 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001299 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001300 // Collections don't include the static data added by SubRoute because
1301 // it has a duplicate entry for members
1302 asyncResp->res.jsonValue["@odata.type"] =
1303 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001304 asyncResp->res.jsonValue["@odata.id"] =
1305 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001306 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001307 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001308 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1309 asyncResp->res.jsonValue["Description"] =
1310 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001311 asyncResp->res.jsonValue["@odata.id"] =
1312 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001313 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1314 logEntryArray = nlohmann::json::array();
1315
1316 // Go through the journal and use the timestamp to create a unique ID
1317 // for each entry
1318 sd_journal *journalTmp = nullptr;
1319 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1320 if (ret < 0)
1321 {
1322 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001323 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001324 return;
1325 }
1326 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1327 journalTmp, sd_journal_close);
1328 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001329 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001330 // Reset the unique ID on the first entry
1331 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001332 SD_JOURNAL_FOREACH(journal.get())
1333 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001334 entryCount++;
1335 // Handle paging using skip (number of entries to skip from the
1336 // start) and top (number of entries to display)
1337 if (entryCount <= skip || entryCount > skip + top)
1338 {
1339 continue;
1340 }
1341
Jason M. Bills16428a12018-11-02 12:42:29 -07001342 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001343 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001344 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001345 continue;
1346 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001347
Jason M. Billse85d6b12019-07-29 17:01:15 -07001348 if (firstEntry)
1349 {
1350 firstEntry = false;
1351 }
1352
Jason M. Billse1f26342018-07-18 12:12:00 -07001353 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001354 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1355 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1356 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001357 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001358 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001359 return;
1360 }
1361 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001362 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1363 if (skip + top < entryCount)
1364 {
1365 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001366 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001367 std::to_string(skip + top);
1368 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001369 }
1370};
1371
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001372class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001373{
1374 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001375 BMCJournalLogEntry(CrowApp &app) :
1376 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001377 std::string())
1378 {
1379 entityPrivileges = {
1380 {boost::beast::http::verb::get, {{"Login"}}},
1381 {boost::beast::http::verb::head, {{"Login"}}},
1382 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1383 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1384 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1385 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1386 }
1387
1388 private:
1389 void doGet(crow::Response &res, const crow::Request &req,
1390 const std::vector<std::string> &params) override
1391 {
1392 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1393 if (params.size() != 1)
1394 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001395 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001396 return;
1397 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001398 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001399 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001400 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001401 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001402 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001403 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001404 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001405 }
1406
1407 sd_journal *journalTmp = nullptr;
1408 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1409 if (ret < 0)
1410 {
1411 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001412 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001413 return;
1414 }
1415 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1416 journalTmp, sd_journal_close);
1417 journalTmp = nullptr;
1418 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001419 // tracking the unique ID
1420 std::string idStr;
1421 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001422 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001423 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001424 {
1425 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001426 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1427 {
1428 messages::internalError(asyncResp->res);
1429 return;
1430 }
1431 if (firstEntry)
1432 {
1433 firstEntry = false;
1434 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001435 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001436 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001437 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001438 {
1439 messages::resourceMissingAtURI(asyncResp->res, entryID);
1440 return;
1441 }
1442
1443 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1444 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001445 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001446 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001447 return;
1448 }
1449 }
1450};
1451
Jason M. Bills424c4172019-03-21 13:50:33 -07001452class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001453{
1454 public:
1455 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001456 CrashdumpService(CrowApp &app) :
1457 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001458 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001459 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001460 {boost::beast::http::verb::get, {{"Login"}}},
1461 {boost::beast::http::verb::head, {{"Login"}}},
1462 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1463 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1464 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1465 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001466 }
1467
1468 private:
1469 /**
1470 * Functions triggers appropriate requests on DBus
1471 */
1472 void doGet(crow::Response &res, const crow::Request &req,
1473 const std::vector<std::string> &params) override
1474 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001475 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001476 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001477 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001478 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001479 asyncResp->res.jsonValue["@odata.type"] =
1480 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001481 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1482 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1483 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001484 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1485 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001486 asyncResp->res.jsonValue["Entries"] = {
1487 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001488 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001489 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001490 {"#LogService.ClearLog",
1491 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1492 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001493 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001494 {{"#Crashdump.OnDemand",
1495 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1496 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001497
1498#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001499 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001500 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001501 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1502 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001503#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001504 }
1505};
1506
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001507class CrashdumpClear : public Node
1508{
1509 public:
1510 CrashdumpClear(CrowApp &app) :
1511 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1512 "LogService.ClearLog/")
1513 {
1514 entityPrivileges = {
1515 {boost::beast::http::verb::get, {{"Login"}}},
1516 {boost::beast::http::verb::head, {{"Login"}}},
1517 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1518 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1519 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1520 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1521 }
1522
1523 private:
1524 void doPost(crow::Response &res, const crow::Request &req,
1525 const std::vector<std::string> &params) override
1526 {
1527 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1528
1529 crow::connections::systemBus->async_method_call(
1530 [asyncResp](const boost::system::error_code ec,
1531 const std::string &resp) {
1532 if (ec)
1533 {
1534 messages::internalError(asyncResp->res);
1535 return;
1536 }
1537 messages::success(asyncResp->res);
1538 },
1539 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
1540 }
1541};
1542
Jason M. Billse855dd22019-10-08 11:37:48 -07001543std::string getLogCreatedTime(const std::string &crashdump)
1544{
1545 nlohmann::json crashdumpJson =
1546 nlohmann::json::parse(crashdump, nullptr, false);
1547 if (crashdumpJson.is_discarded())
1548 {
1549 return std::string();
1550 }
1551
1552 nlohmann::json::const_iterator cdIt = crashdumpJson.find("crash_data");
1553 if (cdIt == crashdumpJson.end())
1554 {
1555 return std::string();
1556 }
1557
1558 nlohmann::json::const_iterator siIt = cdIt->find("METADATA");
1559 if (siIt == cdIt->end())
1560 {
1561 return std::string();
1562 }
1563
1564 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1565 if (tsIt == siIt->end())
1566 {
1567 return std::string();
1568 }
1569
1570 const std::string *logTime = tsIt->get_ptr<const std::string *>();
1571 if (logTime == nullptr)
1572 {
1573 return std::string();
1574 }
1575
1576 std::string redfishDateTime = *logTime;
1577 if (redfishDateTime.length() > 2)
1578 {
1579 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1580 }
1581
1582 return redfishDateTime;
1583}
1584
1585std::string getLogFileName(const std::string &logTime)
1586{
1587 // Set the crashdump file name to "crashdump_<logTime>.json" using the
1588 // created time without the timezone info
1589 std::string fileTime = logTime;
1590 size_t plusPos = fileTime.rfind('+');
1591 if (plusPos != std::string::npos)
1592 {
1593 fileTime.erase(plusPos);
1594 }
1595 return "crashdump_" + fileTime + ".json";
1596}
1597
1598static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1599 const std::string &logID,
1600 nlohmann::json &logEntryJson)
1601{
1602 auto getStoredLogCallback = [asyncResp, logID, &logEntryJson](
1603 const boost::system::error_code ec,
1604 const std::variant<std::string> &resp) {
1605 if (ec)
1606 {
1607 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001608 if (ec.value() ==
1609 boost::system::linux_error::bad_request_descriptor)
1610 {
1611 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
1612 }
1613 else
1614 {
1615 messages::internalError(asyncResp->res);
1616 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001617 return;
1618 }
1619 const std::string *log = std::get_if<std::string>(&resp);
1620 if (log == nullptr)
1621 {
1622 messages::internalError(asyncResp->res);
1623 return;
1624 }
1625 std::string logTime = getLogCreatedTime(*log);
1626 std::string fileName = getLogFileName(logTime);
1627
1628 logEntryJson = {
1629 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse855dd22019-10-08 11:37:48 -07001630 {"@odata.id",
1631 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1632 logID},
1633 {"Name", "CPU Crashdump"},
1634 {"Id", logID},
1635 {"EntryType", "Oem"},
1636 {"OemRecordFormat", "Crashdump URI"},
1637 {"Message",
1638 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1639 logID + "/" + fileName},
1640 {"Created", std::move(logTime)}};
1641 };
1642 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001643 std::move(getStoredLogCallback), crashdumpObject,
1644 crashdumpPath + std::string("/") + logID,
1645 "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, "Log");
Jason M. Billse855dd22019-10-08 11:37:48 -07001646}
1647
Jason M. Bills424c4172019-03-21 13:50:33 -07001648class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001649{
1650 public:
1651 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001652 CrashdumpEntryCollection(CrowApp &app) :
1653 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001654 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001655 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001656 {boost::beast::http::verb::get, {{"Login"}}},
1657 {boost::beast::http::verb::head, {{"Login"}}},
1658 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1659 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1660 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1661 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001662 }
1663
1664 private:
1665 /**
1666 * Functions triggers appropriate requests on DBus
1667 */
1668 void doGet(crow::Response &res, const crow::Request &req,
1669 const std::vector<std::string> &params) override
1670 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001671 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001672 // Collections don't include the static data added by SubRoute because
1673 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001674 auto getLogEntriesCallback = [asyncResp](
1675 const boost::system::error_code ec,
1676 const std::vector<std::string> &resp) {
1677 if (ec)
1678 {
1679 if (ec.value() !=
1680 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001681 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001682 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1683 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001684 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001685 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001686 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001687 }
1688 asyncResp->res.jsonValue["@odata.type"] =
1689 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001690 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001691 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07001692 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001693 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001694 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001695 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1696 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07001697 std::vector<std::string> logIDs;
1698 // Get the list of log entries and build up an empty array big
1699 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07001700 for (const std::string &objpath : resp)
1701 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001702 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07001703 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07001704 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07001705 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001706 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07001707 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001708 logIDs.emplace_back(objpath.substr(lastPos + 1));
1709
1710 // Add a space for the log entry to the array
1711 logEntryArray.push_back({});
1712 }
1713 // Now go through and set up async calls to fill in the entries
1714 size_t index = 0;
1715 for (const std::string &logID : logIDs)
1716 {
1717 // Add the log entry to the array
1718 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07001719 }
1720 asyncResp->res.jsonValue["Members@odata.count"] =
1721 logEntryArray.size();
1722 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001723 crow::connections::systemBus->async_method_call(
1724 std::move(getLogEntriesCallback),
1725 "xyz.openbmc_project.ObjectMapper",
1726 "/xyz/openbmc_project/object_mapper",
1727 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001728 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001729 }
1730};
1731
Jason M. Bills424c4172019-03-21 13:50:33 -07001732class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001733{
1734 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001735 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001736 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001737 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001738 std::string())
1739 {
1740 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001741 {boost::beast::http::verb::get, {{"Login"}}},
1742 {boost::beast::http::verb::head, {{"Login"}}},
1743 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1744 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1745 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1746 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001747 }
1748
1749 private:
1750 void doGet(crow::Response &res, const crow::Request &req,
1751 const std::vector<std::string> &params) override
1752 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001753 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001754 if (params.size() != 1)
1755 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001756 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001757 return;
1758 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001759 const std::string &logID = params[0];
1760 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
1761 }
1762};
1763
1764class CrashdumpFile : public Node
1765{
1766 public:
1767 CrashdumpFile(CrowApp &app) :
1768 Node(app,
1769 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
1770 "<str>/",
1771 std::string(), std::string())
1772 {
1773 entityPrivileges = {
1774 {boost::beast::http::verb::get, {{"Login"}}},
1775 {boost::beast::http::verb::head, {{"Login"}}},
1776 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1777 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1778 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1779 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1780 }
1781
1782 private:
1783 void doGet(crow::Response &res, const crow::Request &req,
1784 const std::vector<std::string> &params) override
1785 {
1786 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1787 if (params.size() != 2)
1788 {
1789 messages::internalError(asyncResp->res);
1790 return;
1791 }
1792 const std::string &logID = params[0];
1793 const std::string &fileName = params[1];
1794
1795 auto getStoredLogCallback = [asyncResp, logID, fileName](
Ed Tanousabf2add2019-01-22 16:40:12 -08001796 const boost::system::error_code ec,
1797 const std::variant<std::string> &resp) {
1798 if (ec)
1799 {
1800 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1801 messages::internalError(asyncResp->res);
1802 return;
1803 }
1804 const std::string *log = std::get_if<std::string>(&resp);
1805 if (log == nullptr)
1806 {
1807 messages::internalError(asyncResp->res);
1808 return;
1809 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001810
1811 // Verify the file name parameter is correct
1812 if (fileName != getLogFileName(getLogCreatedTime(*log)))
Ed Tanousabf2add2019-01-22 16:40:12 -08001813 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001814 messages::resourceMissingAtURI(asyncResp->res, fileName);
Ed Tanousabf2add2019-01-22 16:40:12 -08001815 return;
1816 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001817
1818 // Configure this to be a file download when accessed from a browser
1819 asyncResp->res.addHeader("Content-Disposition", "attachment");
1820 asyncResp->res.body() = *log;
Ed Tanousabf2add2019-01-22 16:40:12 -08001821 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001822 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001823 std::move(getStoredLogCallback), crashdumpObject,
1824 crashdumpPath + std::string("/") + logID,
1825 "org.freedesktop.DBus.Properties", "Get", crashdumpInterface,
Jason M. Bills424c4172019-03-21 13:50:33 -07001826 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001827 }
1828};
1829
Jason M. Bills424c4172019-03-21 13:50:33 -07001830class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001831{
1832 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001833 OnDemandCrashdump(CrowApp &app) :
1834 Node(app,
1835 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1836 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001837 {
1838 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001839 {boost::beast::http::verb::get, {{"Login"}}},
1840 {boost::beast::http::verb::head, {{"Login"}}},
1841 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1842 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1843 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1844 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001845 }
1846
1847 private:
1848 void doPost(crow::Response &res, const crow::Request &req,
1849 const std::vector<std::string> &params) override
1850 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001851 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001852
James Feist46229572020-02-19 15:11:58 -08001853 auto generateonDemandLogCallback = [asyncResp](
1854 const boost::system::error_code
1855 ec,
1856 const std::string &resp) {
1857 if (ec)
1858 {
1859 if (ec.value() == boost::system::errc::operation_not_supported)
Ed Tanous1da66f72018-07-27 16:13:37 -07001860 {
James Feist46229572020-02-19 15:11:58 -08001861 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001862 }
James Feist46229572020-02-19 15:11:58 -08001863 else if (ec.value() ==
1864 boost::system::errc::device_or_resource_busy)
1865 {
1866 messages::serviceTemporarilyUnavailable(asyncResp->res,
1867 "60");
1868 }
1869 else
1870 {
1871 messages::internalError(asyncResp->res);
1872 }
1873 return;
1874 }
1875 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
James Feist66afe4f2020-02-24 13:09:58 -08001876 [](boost::system::error_code err, sdbusplus::message::message &,
1877 const std::shared_ptr<task::TaskData> &taskData) {
1878 if (!err)
1879 {
1880 taskData->messages.emplace_back(messages::success());
James Feist32898ce2020-03-10 16:16:52 -07001881 taskData->status = "Completed";
James Feist66afe4f2020-02-24 13:09:58 -08001882 }
James Feist32898ce2020-03-10 16:16:52 -07001883 return task::completed;
James Feist66afe4f2020-02-24 13:09:58 -08001884 },
James Feist46229572020-02-19 15:11:58 -08001885 "type='signal',interface='org.freedesktop.DBus.Properties',"
1886 "member='PropertiesChanged',arg0namespace='com.intel."
1887 "crashdump'");
1888 task->startTimer(std::chrono::minutes(5));
1889 task->populateResp(asyncResp->res);
1890 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001891 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001892 std::move(generateonDemandLogCallback), crashdumpObject,
1893 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001894 }
1895};
1896
Jason M. Billse1f26342018-07-18 12:12:00 -07001897class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001898{
1899 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001900 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001901 Node(app,
1902 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1903 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001904 {
1905 entityPrivileges = {
1906 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1907 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1908 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1909 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1910 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1911 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1912 }
1913
1914 private:
1915 void doPost(crow::Response &res, const crow::Request &req,
1916 const std::vector<std::string> &params) override
1917 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001918 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001919 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07001920
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001921 nlohmann::json reqJson =
1922 nlohmann::json::parse(req.body, nullptr, false);
1923 if (reqJson.find("PECICommands") != reqJson.end())
1924 {
1925 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
1926 {
1927 return;
1928 }
1929 uint32_t idx = 0;
1930 for (auto const &cmd : peciCommands)
1931 {
1932 if (cmd.size() < 3)
1933 {
1934 std::string s("[");
1935 for (auto const &val : cmd)
1936 {
1937 if (val != *cmd.begin())
1938 {
1939 s += ",";
1940 }
1941 s += std::to_string(val);
1942 }
1943 s += "]";
1944 messages::actionParameterValueFormatError(
1945 res, s, "PECICommands[" + std::to_string(idx) + "]",
1946 "SendRawPeci");
1947 return;
1948 }
1949 idx++;
1950 }
1951 }
1952 else
1953 {
1954 /* This interface is deprecated */
1955 uint8_t clientAddress = 0;
1956 uint8_t readLength = 0;
1957 std::vector<uint8_t> peciCommand;
1958 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1959 "ReadLength", readLength, "PECICommand",
1960 peciCommand))
1961 {
1962 return;
1963 }
1964 peciCommands.push_back({clientAddress, 0, readLength});
1965 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
1966 peciCommand.end());
1967 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001968 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001969 auto sendRawPECICallback =
1970 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001971 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07001972 if (ec)
1973 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001974 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07001975 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001976 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001977 return;
1978 }
1979 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1980 {"PECIResponse", resp}};
1981 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001982 // Call the SendRawPECI command with the provided data
1983 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001984 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001985 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07001986 }
1987};
1988
Andrew Geisslercb92c032018-08-17 07:56:14 -07001989/**
1990 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1991 */
1992class DBusLogServiceActionsClear : public Node
1993{
1994 public:
1995 DBusLogServiceActionsClear(CrowApp &app) :
1996 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Tim Lee1f56a3a2019-10-09 10:17:57 +08001997 "LogService.ClearLog")
Andrew Geisslercb92c032018-08-17 07:56:14 -07001998 {
1999 entityPrivileges = {
2000 {boost::beast::http::verb::get, {{"Login"}}},
2001 {boost::beast::http::verb::head, {{"Login"}}},
2002 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2003 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2004 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2005 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2006 }
2007
2008 private:
2009 /**
2010 * Function handles POST method request.
2011 * The Clear Log actions does not require any parameter.The action deletes
2012 * all entries found in the Entries collection for this Log Service.
2013 */
2014 void doPost(crow::Response &res, const crow::Request &req,
2015 const std::vector<std::string> &params) override
2016 {
2017 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2018
2019 auto asyncResp = std::make_shared<AsyncResp>(res);
2020 // Process response from Logging service.
2021 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2022 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2023 if (ec)
2024 {
2025 // TODO Handle for specific error code
2026 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2027 asyncResp->res.result(
2028 boost::beast::http::status::internal_server_error);
2029 return;
2030 }
2031
2032 asyncResp->res.result(boost::beast::http::status::no_content);
2033 };
2034
2035 // Make call to Logging service to request Clear Log
2036 crow::connections::systemBus->async_method_call(
2037 resp_handler, "xyz.openbmc_project.Logging",
2038 "/xyz/openbmc_project/logging",
2039 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2040 }
2041};
Ed Tanous1da66f72018-07-27 16:13:37 -07002042} // namespace redfish