blob: 9fd35a922ab5ee39f7814966c2f052e992400073 [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"
Ed Tanous1da66f72018-07-27 16:13:37 -070022
Jason M. Billse1f26342018-07-18 12:12:00 -070023#include <systemd/sd-journal.h>
24
Jason M. Bills4851d452019-03-28 11:27:48 -070025#include <boost/algorithm/string/split.hpp>
26#include <boost/beast/core/span.hpp>
Ed Tanous1da66f72018-07-27 16:13:37 -070027#include <boost/container/flat_map.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070028#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070029#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070030#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080031#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070032
33namespace redfish
34{
35
Jason M. Bills424c4172019-03-21 13:50:33 -070036constexpr char const *CrashdumpObject = "com.intel.crashdump";
37constexpr char const *CrashdumpPath = "/com/intel/crashdump";
38constexpr char const *CrashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
39constexpr char const *CrashdumpInterface = "com.intel.crashdump";
40constexpr char const *CrashdumpOnDemandInterface =
41 "com.intel.crashdump.OnDemand";
42constexpr char const *CrashdumpRawPECIInterface =
43 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070044
Jason M. Bills4851d452019-03-28 11:27:48 -070045namespace message_registries
46{
47static const Message *getMessageFromRegistry(
48 const std::string &messageKey,
49 const boost::beast::span<const MessageEntry> registry)
50{
51 boost::beast::span<const MessageEntry>::const_iterator messageIt =
52 std::find_if(registry.cbegin(), registry.cend(),
53 [&messageKey](const MessageEntry &messageEntry) {
54 return !std::strcmp(messageEntry.first,
55 messageKey.c_str());
56 });
57 if (messageIt != registry.cend())
58 {
59 return &messageIt->second;
60 }
61
62 return nullptr;
63}
64
65static const Message *getMessage(const std::string_view &messageID)
66{
67 // Redfish MessageIds are in the form
68 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
69 // the right Message
70 std::vector<std::string> fields;
71 fields.reserve(4);
72 boost::split(fields, messageID, boost::is_any_of("."));
73 std::string &registryName = fields[0];
74 std::string &messageKey = fields[3];
75
76 // Find the right registry and check it for the MessageKey
77 if (std::string(base::header.registryPrefix) == registryName)
78 {
79 return getMessageFromRegistry(
80 messageKey, boost::beast::span<const MessageEntry>(base::registry));
81 }
82 if (std::string(openbmc::header.registryPrefix) == registryName)
83 {
84 return getMessageFromRegistry(
85 messageKey,
86 boost::beast::span<const MessageEntry>(openbmc::registry));
87 }
88 return nullptr;
89}
90} // namespace message_registries
91
James Feistf6150402019-01-08 10:36:20 -080092namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070093
Andrew Geisslercb92c032018-08-17 07:56:14 -070094using GetManagedPropertyType = boost::container::flat_map<
95 std::string,
96 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
97 int32_t, uint32_t, int64_t, uint64_t, double>>;
98
99using GetManagedObjectsType = boost::container::flat_map<
100 sdbusplus::message::object_path,
101 boost::container::flat_map<std::string, GetManagedPropertyType>>;
102
103inline std::string translateSeverityDbusToRedfish(const std::string &s)
104{
105 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
106 {
107 return "Critical";
108 }
109 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
110 {
111 return "Critical";
112 }
113 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
114 {
115 return "OK";
116 }
117 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
118 {
119 return "Critical";
120 }
121 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
122 {
123 return "Critical";
124 }
125 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
126 {
127 return "OK";
128 }
129 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
130 {
131 return "OK";
132 }
133 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
134 {
135 return "Warning";
136 }
137 return "";
138}
139
Jason M. Bills16428a12018-11-02 12:42:29 -0700140static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800141 const std::string_view &field,
142 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700143{
144 const char *data = nullptr;
145 size_t length = 0;
146 int ret = 0;
147 // Get the metadata from the requested field of the journal entry
Ed Tanousb01bf292019-03-25 19:25:26 +0000148 ret = sd_journal_get_data(journal, field.data(), (const void **)&data,
149 &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700150 if (ret < 0)
151 {
152 return ret;
153 }
Ed Tanous39e77502019-03-04 17:35:53 -0800154 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700155 // Only use the content after the "=" character.
156 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
157 return ret;
158}
159
160static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800161 const std::string_view &field, const int &base,
Jason M. Bills16428a12018-11-02 12:42:29 -0700162 int &contents)
163{
164 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800165 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700166 // Get the metadata from the requested field of the journal entry
167 ret = getJournalMetadata(journal, field, metadata);
168 if (ret < 0)
169 {
170 return ret;
171 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000172 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700173 return ret;
174}
175
176static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
177{
178 int ret = 0;
179 uint64_t timestamp = 0;
180 ret = sd_journal_get_realtime_usec(journal, &timestamp);
181 if (ret < 0)
182 {
183 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
184 << strerror(-ret);
185 return false;
186 }
187 time_t t =
188 static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
189 struct tm *loctime = localtime(&t);
190 char entryTime[64] = {};
191 if (NULL != loctime)
192 {
193 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
194 }
195 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800196 std::string_view t1(entryTime);
197 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700198 if (t1.size() > 2 && t2.size() > 2)
199 {
200 t1.remove_suffix(2);
201 t2.remove_prefix(t2.size() - 2);
202 }
Ed Tanous39e77502019-03-04 17:35:53 -0800203 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700204 return true;
205}
206
207static bool getSkipParam(crow::Response &res, const crow::Request &req,
208 long &skip)
209{
210 char *skipParam = req.urlParams.get("$skip");
211 if (skipParam != nullptr)
212 {
213 char *ptr = nullptr;
214 skip = std::strtol(skipParam, &ptr, 10);
215 if (*skipParam == '\0' || *ptr != '\0')
216 {
217
218 messages::queryParameterValueTypeError(res, std::string(skipParam),
219 "$skip");
220 return false;
221 }
222 if (skip < 0)
223 {
224
225 messages::queryParameterOutOfRange(res, std::to_string(skip),
226 "$skip", "greater than 0");
227 return false;
228 }
229 }
230 return true;
231}
232
233static constexpr const long maxEntriesPerPage = 1000;
234static bool getTopParam(crow::Response &res, const crow::Request &req,
235 long &top)
236{
237 char *topParam = req.urlParams.get("$top");
238 if (topParam != nullptr)
239 {
240 char *ptr = nullptr;
241 top = std::strtol(topParam, &ptr, 10);
242 if (*topParam == '\0' || *ptr != '\0')
243 {
244 messages::queryParameterValueTypeError(res, std::string(topParam),
245 "$top");
246 return false;
247 }
248 if (top < 1 || top > maxEntriesPerPage)
249 {
250
251 messages::queryParameterOutOfRange(
252 res, std::to_string(top), "$top",
253 "1-" + std::to_string(maxEntriesPerPage));
254 return false;
255 }
256 }
257 return true;
258}
259
Jason M. Billse85d6b12019-07-29 17:01:15 -0700260static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
261 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700262{
263 int ret = 0;
264 static uint64_t prevTs = 0;
265 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700266 if (firstEntry)
267 {
268 prevTs = 0;
269 }
270
Jason M. Bills16428a12018-11-02 12:42:29 -0700271 // Get the entry timestamp
272 uint64_t curTs = 0;
273 ret = sd_journal_get_realtime_usec(journal, &curTs);
274 if (ret < 0)
275 {
276 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
277 << strerror(-ret);
278 return false;
279 }
280 // If the timestamp isn't unique, increment the index
281 if (curTs == prevTs)
282 {
283 index++;
284 }
285 else
286 {
287 // Otherwise, reset it
288 index = 0;
289 }
290 // Save the timestamp
291 prevTs = curTs;
292
293 entryID = std::to_string(curTs);
294 if (index > 0)
295 {
296 entryID += "_" + std::to_string(index);
297 }
298 return true;
299}
300
Jason M. Billse85d6b12019-07-29 17:01:15 -0700301static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
302 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700303{
304 static uint64_t prevTs = 0;
305 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700306 if (firstEntry)
307 {
308 prevTs = 0;
309 }
310
Jason M. Bills95820182019-04-22 16:25:34 -0700311 // Get the entry timestamp
312 uint64_t curTs = 0;
313 std::tm timeStruct = {};
314 std::istringstream entryStream(logEntry);
315 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
316 {
317 curTs = std::mktime(&timeStruct);
318 }
319 // If the timestamp isn't unique, increment the index
320 if (curTs == prevTs)
321 {
322 index++;
323 }
324 else
325 {
326 // Otherwise, reset it
327 index = 0;
328 }
329 // Save the timestamp
330 prevTs = curTs;
331
332 entryID = std::to_string(curTs);
333 if (index > 0)
334 {
335 entryID += "_" + std::to_string(index);
336 }
337 return true;
338}
339
Jason M. Bills16428a12018-11-02 12:42:29 -0700340static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
341 uint64_t &timestamp, uint16_t &index)
342{
343 if (entryID.empty())
344 {
345 return false;
346 }
347 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800348 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700349
350 auto underscorePos = tsStr.find("_");
351 if (underscorePos != tsStr.npos)
352 {
353 // Timestamp has an index
354 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800355 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700356 indexStr.remove_prefix(underscorePos + 1);
357 std::size_t pos;
358 try
359 {
Ed Tanous39e77502019-03-04 17:35:53 -0800360 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700361 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000362 catch (std::invalid_argument)
Jason M. Bills16428a12018-11-02 12:42:29 -0700363 {
364 messages::resourceMissingAtURI(res, entryID);
365 return false;
366 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000367 catch (std::out_of_range)
Jason M. Bills16428a12018-11-02 12:42:29 -0700368 {
369 messages::resourceMissingAtURI(res, entryID);
370 return false;
371 }
372 if (pos != indexStr.size())
373 {
374 messages::resourceMissingAtURI(res, entryID);
375 return false;
376 }
377 }
378 // Timestamp has no index
379 std::size_t pos;
380 try
381 {
Ed Tanous39e77502019-03-04 17:35:53 -0800382 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700383 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000384 catch (std::invalid_argument)
Jason M. Bills16428a12018-11-02 12:42:29 -0700385 {
386 messages::resourceMissingAtURI(res, entryID);
387 return false;
388 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000389 catch (std::out_of_range)
Jason M. Bills16428a12018-11-02 12:42:29 -0700390 {
391 messages::resourceMissingAtURI(res, entryID);
392 return false;
393 }
394 if (pos != tsStr.size())
395 {
396 messages::resourceMissingAtURI(res, entryID);
397 return false;
398 }
399 return true;
400}
401
Jason M. Bills95820182019-04-22 16:25:34 -0700402static bool
403 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
404{
405 static const std::filesystem::path redfishLogDir = "/var/log";
406 static const std::string redfishLogFilename = "redfish";
407
408 // Loop through the directory looking for redfish log files
409 for (const std::filesystem::directory_entry &dirEnt :
410 std::filesystem::directory_iterator(redfishLogDir))
411 {
412 // If we find a redfish log file, save the path
413 std::string filename = dirEnt.path().filename();
414 if (boost::starts_with(filename, redfishLogFilename))
415 {
416 redfishLogFiles.emplace_back(redfishLogDir / filename);
417 }
418 }
419 // As the log files rotate, they are appended with a ".#" that is higher for
420 // the older logs. Since we don't expect more than 10 log files, we
421 // can just sort the list to get them in order from newest to oldest
422 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
423
424 return !redfishLogFiles.empty();
425}
426
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800427class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700428{
429 public:
430 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800431 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800432 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800433 {
434 entityPrivileges = {
435 {boost::beast::http::verb::get, {{"Login"}}},
436 {boost::beast::http::verb::head, {{"Login"}}},
437 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
438 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
439 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
440 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
441 }
442
443 private:
444 /**
445 * Functions triggers appropriate requests on DBus
446 */
447 void doGet(crow::Response &res, const crow::Request &req,
448 const std::vector<std::string> &params) override
449 {
450 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800451 // Collections don't include the static data added by SubRoute because
452 // it has a duplicate entry for members
453 asyncResp->res.jsonValue["@odata.type"] =
454 "#LogServiceCollection.LogServiceCollection";
455 asyncResp->res.jsonValue["@odata.context"] =
456 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
457 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800458 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800459 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
460 asyncResp->res.jsonValue["Description"] =
461 "Collection of LogServices for this Computer System";
462 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
463 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800464 logServiceArray.push_back(
465 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800466#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
467 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500468 {{"@odata.id",
469 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800470#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800471 asyncResp->res.jsonValue["Members@odata.count"] =
472 logServiceArray.size();
473 }
474};
475
476class EventLogService : public Node
477{
478 public:
479 template <typename CrowApp>
480 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800481 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800482 {
483 entityPrivileges = {
484 {boost::beast::http::verb::get, {{"Login"}}},
485 {boost::beast::http::verb::head, {{"Login"}}},
486 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
487 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
488 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
489 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
490 }
491
492 private:
493 void doGet(crow::Response &res, const crow::Request &req,
494 const std::vector<std::string> &params) override
495 {
496 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
497
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800498 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800499 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800500 asyncResp->res.jsonValue["@odata.type"] =
501 "#LogService.v1_1_0.LogService";
502 asyncResp->res.jsonValue["@odata.context"] =
503 "/redfish/v1/$metadata#LogService.LogService";
504 asyncResp->res.jsonValue["Name"] = "Event Log Service";
505 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
506 asyncResp->res.jsonValue["Id"] = "Event Log";
507 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
508 asyncResp->res.jsonValue["Entries"] = {
509 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800510 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500511 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
512
513 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
514 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700515 }
516};
517
518class EventLogClear : public Node
519{
520 public:
521 EventLogClear(CrowApp &app) :
522 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
523 "LogService.ClearLog/")
524 {
525 entityPrivileges = {
526 {boost::beast::http::verb::get, {{"Login"}}},
527 {boost::beast::http::verb::head, {{"Login"}}},
528 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
529 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
530 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
531 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
532 }
533
534 private:
535 void doPost(crow::Response &res, const crow::Request &req,
536 const std::vector<std::string> &params) override
537 {
538 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
539
540 // Clear the EventLog by deleting the log files
541 std::vector<std::filesystem::path> redfishLogFiles;
542 if (getRedfishLogFiles(redfishLogFiles))
543 {
544 for (const std::filesystem::path &file : redfishLogFiles)
545 {
546 std::error_code ec;
547 std::filesystem::remove(file, ec);
548 }
549 }
550
551 // Reload rsyslog so it knows to start new log files
552 crow::connections::systemBus->async_method_call(
553 [asyncResp](const boost::system::error_code ec) {
554 if (ec)
555 {
556 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
557 messages::internalError(asyncResp->res);
558 return;
559 }
560
561 messages::success(asyncResp->res);
562 },
563 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
564 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
565 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800566 }
567};
568
Jason M. Bills95820182019-04-22 16:25:34 -0700569static int fillEventLogEntryJson(const std::string &logEntryID,
570 const std::string logEntry,
571 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800572{
Jason M. Bills95820182019-04-22 16:25:34 -0700573 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700574 // First get the Timestamp
575 size_t space = logEntry.find_first_of(" ");
576 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700577 {
578 return 1;
579 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700580 std::string timestamp = logEntry.substr(0, space);
581 // Then get the log contents
582 size_t entryStart = logEntry.find_first_not_of(" ", space);
583 if (entryStart == std::string::npos)
584 {
585 return 1;
586 }
587 std::string_view entry(logEntry);
588 entry.remove_prefix(entryStart);
589 // Use split to separate the entry into its fields
590 std::vector<std::string> logEntryFields;
591 boost::split(logEntryFields, entry, boost::is_any_of(","),
592 boost::token_compress_on);
593 // We need at least a MessageId to be valid
594 if (logEntryFields.size() < 1)
595 {
596 return 1;
597 }
598 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700599
Jason M. Bills4851d452019-03-28 11:27:48 -0700600 // Get the Message from the MessageRegistry
601 const message_registries::Message *message =
602 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800603
Jason M. Bills4851d452019-03-28 11:27:48 -0700604 std::string msg;
605 std::string severity;
606 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800607 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700608 msg = message->message;
609 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800610 }
611
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700612 // Get the MessageArgs from the log if there are any
613 boost::beast::span<std::string> messageArgs;
614 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700615 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700616 std::string &messageArgsStart = logEntryFields[1];
617 // If the first string is empty, assume there are no MessageArgs
618 std::size_t messageArgsSize = 0;
619 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700620 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700621 messageArgsSize = logEntryFields.size() - 1;
622 }
623
624 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
625
626 // Fill the MessageArgs into the Message
627 int i = 0;
628 for (const std::string &messageArg : messageArgs)
629 {
630 std::string argStr = "%" + std::to_string(++i);
631 size_t argPos = msg.find(argStr);
632 if (argPos != std::string::npos)
633 {
634 msg.replace(argPos, argStr.length(), messageArg);
635 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700636 }
637 }
638
Jason M. Bills95820182019-04-22 16:25:34 -0700639 // Get the Created time from the timestamp. The log timestamp is in RFC3339
640 // format which matches the Redfish format except for the fractional seconds
641 // between the '.' and the '+', so just remove them.
642 std::size_t dot = timestamp.find_first_of(".");
643 std::size_t plus = timestamp.find_first_of("+");
644 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800645 {
Jason M. Bills95820182019-04-22 16:25:34 -0700646 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800647 }
648
649 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700650 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700651 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800652 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800653 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700654 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700655 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800656 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700657 {"Id", logEntryID},
658 {"Message", std::move(msg)},
659 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800660 {"MessageArgs", std::move(messageArgs)},
661 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700662 {"Severity", std::move(severity)},
663 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800664 return 0;
665}
666
Anthony Wilson27062602019-04-22 02:10:09 -0500667class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800668{
669 public:
670 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500671 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800672 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800673 {
674 entityPrivileges = {
675 {boost::beast::http::verb::get, {{"Login"}}},
676 {boost::beast::http::verb::head, {{"Login"}}},
677 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
678 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
679 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
680 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
681 }
682
683 private:
684 void doGet(crow::Response &res, const crow::Request &req,
685 const std::vector<std::string> &params) override
686 {
687 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
688 long skip = 0;
689 long top = maxEntriesPerPage; // Show max entries by default
690 if (!getSkipParam(asyncResp->res, req, skip))
691 {
692 return;
693 }
694 if (!getTopParam(asyncResp->res, req, top))
695 {
696 return;
697 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800698 // Collections don't include the static data added by SubRoute because
699 // it has a duplicate entry for members
700 asyncResp->res.jsonValue["@odata.type"] =
701 "#LogEntryCollection.LogEntryCollection";
702 asyncResp->res.jsonValue["@odata.context"] =
703 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
704 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800705 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800706 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
707 asyncResp->res.jsonValue["Description"] =
708 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700709
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800710 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
711 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700712 // Go through the log files and create a unique ID for each entry
713 std::vector<std::filesystem::path> redfishLogFiles;
714 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000715 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700716 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700717
718 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700719 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
720 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800721 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700722 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700723 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800724 {
725 continue;
726 }
727
Jason M. Billse85d6b12019-07-29 17:01:15 -0700728 // Reset the unique ID on the first entry
729 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700730 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800731 {
Jason M. Bills95820182019-04-22 16:25:34 -0700732 entryCount++;
733 // Handle paging using skip (number of entries to skip from the
734 // start) and top (number of entries to display)
735 if (entryCount <= skip || entryCount > skip + top)
736 {
737 continue;
738 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800739
Jason M. Bills95820182019-04-22 16:25:34 -0700740 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700741 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700742 {
743 continue;
744 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800745
Jason M. Billse85d6b12019-07-29 17:01:15 -0700746 if (firstEntry)
747 {
748 firstEntry = false;
749 }
750
Jason M. Bills95820182019-04-22 16:25:34 -0700751 logEntryArray.push_back({});
752 nlohmann::json &bmcLogEntry = logEntryArray.back();
753 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
754 {
755 messages::internalError(asyncResp->res);
756 return;
757 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800758 }
759 }
760 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
761 if (skip + top < entryCount)
762 {
763 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700764 "/redfish/v1/Systems/system/LogServices/EventLog/"
765 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800766 std::to_string(skip + top);
767 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500768 }
769};
770
Jason M. Bills897967d2019-07-29 17:05:30 -0700771class JournalEventLogEntry : public Node
772{
773 public:
774 JournalEventLogEntry(CrowApp &app) :
775 Node(app,
776 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
777 std::string())
778 {
779 entityPrivileges = {
780 {boost::beast::http::verb::get, {{"Login"}}},
781 {boost::beast::http::verb::head, {{"Login"}}},
782 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
783 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
784 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
785 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
786 }
787
788 private:
789 void doGet(crow::Response &res, const crow::Request &req,
790 const std::vector<std::string> &params) override
791 {
792 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
793 if (params.size() != 1)
794 {
795 messages::internalError(asyncResp->res);
796 return;
797 }
798 const std::string &targetID = params[0];
799
800 // Go through the log files and check the unique ID for each entry to
801 // find the target entry
802 std::vector<std::filesystem::path> redfishLogFiles;
803 getRedfishLogFiles(redfishLogFiles);
804 std::string logEntry;
805
806 // Oldest logs are in the last file, so start there and loop backwards
807 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
808 it++)
809 {
810 std::ifstream logStream(*it);
811 if (!logStream.is_open())
812 {
813 continue;
814 }
815
816 // Reset the unique ID on the first entry
817 bool firstEntry = true;
818 while (std::getline(logStream, logEntry))
819 {
820 std::string idStr;
821 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
822 {
823 continue;
824 }
825
826 if (firstEntry)
827 {
828 firstEntry = false;
829 }
830
831 if (idStr == targetID)
832 {
833 if (fillEventLogEntryJson(idStr, logEntry,
834 asyncResp->res.jsonValue) != 0)
835 {
836 messages::internalError(asyncResp->res);
837 return;
838 }
839 return;
840 }
841 }
842 }
843 // Requested ID was not found
844 messages::resourceMissingAtURI(asyncResp->res, targetID);
845 }
846};
847
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500848class DBusEventLogEntryCollection : public Node
849{
850 public:
851 template <typename CrowApp>
852 DBusEventLogEntryCollection(CrowApp &app) :
853 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
854 {
855 entityPrivileges = {
856 {boost::beast::http::verb::get, {{"Login"}}},
857 {boost::beast::http::verb::head, {{"Login"}}},
858 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
859 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
860 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
861 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
862 }
863
864 private:
865 void doGet(crow::Response &res, const crow::Request &req,
866 const std::vector<std::string> &params) override
867 {
868 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
869
870 // Collections don't include the static data added by SubRoute because
871 // it has a duplicate entry for members
872 asyncResp->res.jsonValue["@odata.type"] =
873 "#LogEntryCollection.LogEntryCollection";
874 asyncResp->res.jsonValue["@odata.context"] =
875 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
876 asyncResp->res.jsonValue["@odata.id"] =
877 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
878 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
879 asyncResp->res.jsonValue["Description"] =
880 "Collection of System Event Log Entries";
881
Andrew Geisslercb92c032018-08-17 07:56:14 -0700882 // DBus implementation of EventLog/Entries
883 // Make call to Logging Service to find all log entry objects
884 crow::connections::systemBus->async_method_call(
885 [asyncResp](const boost::system::error_code ec,
886 GetManagedObjectsType &resp) {
887 if (ec)
888 {
889 // TODO Handle for specific error code
890 BMCWEB_LOG_ERROR
891 << "getLogEntriesIfaceData resp_handler got error "
892 << ec;
893 messages::internalError(asyncResp->res);
894 return;
895 }
896 nlohmann::json &entriesArray =
897 asyncResp->res.jsonValue["Members"];
898 entriesArray = nlohmann::json::array();
899 for (auto &objectPath : resp)
900 {
901 for (auto &interfaceMap : objectPath.second)
902 {
903 if (interfaceMap.first !=
904 "xyz.openbmc_project.Logging.Entry")
905 {
906 BMCWEB_LOG_DEBUG << "Bailing early on "
907 << interfaceMap.first;
908 continue;
909 }
910 entriesArray.push_back({});
911 nlohmann::json &thisEntry = entriesArray.back();
912 uint32_t *id;
913 std::time_t timestamp;
914 std::string *severity, *message;
915 bool *resolved;
916 for (auto &propertyMap : interfaceMap.second)
917 {
918 if (propertyMap.first == "Id")
919 {
920 id = sdbusplus::message::variant_ns::get_if<
921 uint32_t>(&propertyMap.second);
922 if (id == nullptr)
923 {
924 messages::propertyMissing(asyncResp->res,
925 "Id");
926 }
927 }
928 else if (propertyMap.first == "Timestamp")
929 {
930 const uint64_t *millisTimeStamp =
931 std::get_if<uint64_t>(&propertyMap.second);
932 if (millisTimeStamp == nullptr)
933 {
934 messages::propertyMissing(asyncResp->res,
935 "Timestamp");
936 }
937 // Retrieve Created property with format:
938 // yyyy-mm-ddThh:mm:ss
939 std::chrono::milliseconds chronoTimeStamp(
940 *millisTimeStamp);
941 timestamp =
942 std::chrono::duration_cast<
943 std::chrono::seconds>(chronoTimeStamp)
944 .count();
945 }
946 else if (propertyMap.first == "Severity")
947 {
948 severity = std::get_if<std::string>(
949 &propertyMap.second);
950 if (severity == nullptr)
951 {
952 messages::propertyMissing(asyncResp->res,
953 "Severity");
954 }
955 }
956 else if (propertyMap.first == "Message")
957 {
958 message = std::get_if<std::string>(
959 &propertyMap.second);
960 if (message == nullptr)
961 {
962 messages::propertyMissing(asyncResp->res,
963 "Message");
964 }
965 }
966 }
967 thisEntry = {
968 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
969 {"@odata.context", "/redfish/v1/"
970 "$metadata#LogEntry.LogEntry"},
971 {"@odata.id",
972 "/redfish/v1/Systems/system/LogServices/EventLog/"
973 "Entries/" +
974 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -0500975 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700976 {"Id", std::to_string(*id)},
977 {"Message", *message},
978 {"EntryType", "Event"},
979 {"Severity",
980 translateSeverityDbusToRedfish(*severity)},
981 {"Created", crow::utility::getDateTime(timestamp)}};
982 }
983 }
984 std::sort(entriesArray.begin(), entriesArray.end(),
985 [](const nlohmann::json &left,
986 const nlohmann::json &right) {
987 return (left["Id"] <= right["Id"]);
988 });
989 asyncResp->res.jsonValue["Members@odata.count"] =
990 entriesArray.size();
991 },
992 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
993 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800994 }
995};
996
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500997class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800998{
999 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001000 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001001 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -08001002 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
1003 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001004 {
1005 entityPrivileges = {
1006 {boost::beast::http::verb::get, {{"Login"}}},
1007 {boost::beast::http::verb::head, {{"Login"}}},
1008 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1009 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1010 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1011 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1012 }
1013
1014 private:
1015 void doGet(crow::Response &res, const crow::Request &req,
1016 const std::vector<std::string> &params) override
1017 {
1018 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001019 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001020 {
1021 messages::internalError(asyncResp->res);
1022 return;
1023 }
Ed Tanous029573d2019-02-01 10:57:49 -08001024 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001025
Andrew Geisslercb92c032018-08-17 07:56:14 -07001026 // DBus implementation of EventLog/Entries
1027 // Make call to Logging Service to find all log entry objects
1028 crow::connections::systemBus->async_method_call(
1029 [asyncResp, entryID](const boost::system::error_code ec,
1030 GetManagedPropertyType &resp) {
1031 if (ec)
1032 {
1033 BMCWEB_LOG_ERROR
1034 << "EventLogEntry (DBus) resp_handler got error " << ec;
1035 messages::internalError(asyncResp->res);
1036 return;
1037 }
1038 uint32_t *id;
1039 std::time_t timestamp;
1040 std::string *severity, *message;
1041 bool *resolved;
1042 for (auto &propertyMap : resp)
1043 {
1044 if (propertyMap.first == "Id")
1045 {
1046 id = std::get_if<uint32_t>(&propertyMap.second);
1047 if (id == nullptr)
1048 {
1049 messages::propertyMissing(asyncResp->res, "Id");
1050 }
1051 }
1052 else if (propertyMap.first == "Timestamp")
1053 {
1054 const uint64_t *millisTimeStamp =
1055 std::get_if<uint64_t>(&propertyMap.second);
1056 if (millisTimeStamp == nullptr)
1057 {
1058 messages::propertyMissing(asyncResp->res,
1059 "Timestamp");
1060 }
1061 // Retrieve Created property with format:
1062 // yyyy-mm-ddThh:mm:ss
1063 std::chrono::milliseconds chronoTimeStamp(
1064 *millisTimeStamp);
1065 timestamp =
1066 std::chrono::duration_cast<std::chrono::seconds>(
1067 chronoTimeStamp)
1068 .count();
1069 }
1070 else if (propertyMap.first == "Severity")
1071 {
1072 severity =
1073 std::get_if<std::string>(&propertyMap.second);
1074 if (severity == nullptr)
1075 {
1076 messages::propertyMissing(asyncResp->res,
1077 "Severity");
1078 }
1079 }
1080 else if (propertyMap.first == "Message")
1081 {
1082 message = std::get_if<std::string>(&propertyMap.second);
1083 if (message == nullptr)
1084 {
1085 messages::propertyMissing(asyncResp->res,
1086 "Message");
1087 }
1088 }
1089 }
1090 asyncResp->res.jsonValue = {
1091 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1092 {"@odata.context", "/redfish/v1/"
1093 "$metadata#LogEntry.LogEntry"},
1094 {"@odata.id",
1095 "/redfish/v1/Systems/system/LogServices/EventLog/"
1096 "Entries/" +
1097 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001098 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001099 {"Id", std::to_string(*id)},
1100 {"Message", *message},
1101 {"EntryType", "Event"},
1102 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001103 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001104 },
1105 "xyz.openbmc_project.Logging",
1106 "/xyz/openbmc_project/logging/entry/" + entryID,
1107 "org.freedesktop.DBus.Properties", "GetAll",
1108 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001109 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001110
1111 void doDelete(crow::Response &res, const crow::Request &req,
1112 const std::vector<std::string> &params) override
1113 {
1114
1115 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1116
1117 auto asyncResp = std::make_shared<AsyncResp>(res);
1118
1119 if (params.size() != 1)
1120 {
1121 messages::internalError(asyncResp->res);
1122 return;
1123 }
1124 std::string entryID = params[0];
1125
1126 dbus::utility::escapePathForDbus(entryID);
1127
1128 // Process response from Logging service.
1129 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1130 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1131 if (ec)
1132 {
1133 // TODO Handle for specific error code
1134 BMCWEB_LOG_ERROR
1135 << "EventLogEntry (DBus) doDelete respHandler got error "
1136 << ec;
1137 asyncResp->res.result(
1138 boost::beast::http::status::internal_server_error);
1139 return;
1140 }
1141
1142 asyncResp->res.result(boost::beast::http::status::ok);
1143 };
1144
1145 // Make call to Logging service to request Delete Log
1146 crow::connections::systemBus->async_method_call(
1147 respHandler, "xyz.openbmc_project.Logging",
1148 "/xyz/openbmc_project/logging/entry/" + entryID,
1149 "xyz.openbmc_project.Object.Delete", "Delete");
1150 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001151};
1152
1153class BMCLogServiceCollection : public Node
1154{
1155 public:
1156 template <typename CrowApp>
1157 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001158 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001159 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001160 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001161 {boost::beast::http::verb::get, {{"Login"}}},
1162 {boost::beast::http::verb::head, {{"Login"}}},
1163 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1164 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1165 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1166 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001167 }
1168
1169 private:
1170 /**
1171 * Functions triggers appropriate requests on DBus
1172 */
1173 void doGet(crow::Response &res, const crow::Request &req,
1174 const std::vector<std::string> &params) override
1175 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001176 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001177 // Collections don't include the static data added by SubRoute because
1178 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001179 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001180 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001181 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001182 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001183 asyncResp->res.jsonValue["@odata.id"] =
1184 "/redfish/v1/Managers/bmc/LogServices";
1185 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1186 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001187 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001188 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1189 logServiceArray = nlohmann::json::array();
1190#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1191 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001192 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001193#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001194 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001195 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001196 }
1197};
1198
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001199class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001200{
1201 public:
1202 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001203 BMCJournalLogService(CrowApp &app) :
1204 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001205 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001206 entityPrivileges = {
1207 {boost::beast::http::verb::get, {{"Login"}}},
1208 {boost::beast::http::verb::head, {{"Login"}}},
1209 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1210 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1211 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1212 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1213 }
1214
1215 private:
1216 void doGet(crow::Response &res, const crow::Request &req,
1217 const std::vector<std::string> &params) override
1218 {
1219 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001220 asyncResp->res.jsonValue["@odata.type"] =
1221 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001222 asyncResp->res.jsonValue["@odata.id"] =
1223 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001224 asyncResp->res.jsonValue["@odata.context"] =
1225 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001226 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1227 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1228 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001229 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001230 asyncResp->res.jsonValue["Entries"] = {
1231 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001232 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001233 }
1234};
1235
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001236static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1237 sd_journal *journal,
1238 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001239{
1240 // Get the Log Entry contents
1241 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001242
Ed Tanous39e77502019-03-04 17:35:53 -08001243 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001244 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001245 if (ret < 0)
1246 {
1247 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1248 return 1;
1249 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001250
1251 // Get the severity from the PRIORITY field
Jason M. Billse1f26342018-07-18 12:12:00 -07001252 int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001253 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001254 if (ret < 0)
1255 {
1256 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001257 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001258
1259 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001260 std::string entryTimeStr;
1261 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001262 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001263 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001264 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001265
1266 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001267 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001268 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001269 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001270 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1271 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001272 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001273 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001274 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001275 {"EntryType", "Oem"},
1276 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001277 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001278 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001279 {"Created", std::move(entryTimeStr)}};
1280 return 0;
1281}
1282
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001283class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001284{
1285 public:
1286 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001287 BMCJournalLogEntryCollection(CrowApp &app) :
1288 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001289 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001290 entityPrivileges = {
1291 {boost::beast::http::verb::get, {{"Login"}}},
1292 {boost::beast::http::verb::head, {{"Login"}}},
1293 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1294 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1295 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1296 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1297 }
1298
1299 private:
1300 void doGet(crow::Response &res, const crow::Request &req,
1301 const std::vector<std::string> &params) override
1302 {
1303 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001304 static constexpr const long maxEntriesPerPage = 1000;
1305 long skip = 0;
1306 long top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001307 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001308 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001309 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001310 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001311 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001312 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001313 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001314 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001315 // Collections don't include the static data added by SubRoute because
1316 // it has a duplicate entry for members
1317 asyncResp->res.jsonValue["@odata.type"] =
1318 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001319 asyncResp->res.jsonValue["@odata.id"] =
1320 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001321 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001322 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001323 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001324 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001325 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1326 asyncResp->res.jsonValue["Description"] =
1327 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001328 asyncResp->res.jsonValue["@odata.id"] =
1329 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001330 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1331 logEntryArray = nlohmann::json::array();
1332
1333 // Go through the journal and use the timestamp to create a unique ID
1334 // for each entry
1335 sd_journal *journalTmp = nullptr;
1336 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1337 if (ret < 0)
1338 {
1339 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001340 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001341 return;
1342 }
1343 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1344 journalTmp, sd_journal_close);
1345 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001346 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001347 // Reset the unique ID on the first entry
1348 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001349 SD_JOURNAL_FOREACH(journal.get())
1350 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001351 entryCount++;
1352 // Handle paging using skip (number of entries to skip from the
1353 // start) and top (number of entries to display)
1354 if (entryCount <= skip || entryCount > skip + top)
1355 {
1356 continue;
1357 }
1358
Jason M. Bills16428a12018-11-02 12:42:29 -07001359 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001360 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001361 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001362 continue;
1363 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001364
Jason M. Billse85d6b12019-07-29 17:01:15 -07001365 if (firstEntry)
1366 {
1367 firstEntry = false;
1368 }
1369
Jason M. Billse1f26342018-07-18 12:12:00 -07001370 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001371 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1372 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1373 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001374 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001375 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001376 return;
1377 }
1378 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001379 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1380 if (skip + top < entryCount)
1381 {
1382 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001383 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001384 std::to_string(skip + top);
1385 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001386 }
1387};
1388
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001389class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001390{
1391 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001392 BMCJournalLogEntry(CrowApp &app) :
1393 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001394 std::string())
1395 {
1396 entityPrivileges = {
1397 {boost::beast::http::verb::get, {{"Login"}}},
1398 {boost::beast::http::verb::head, {{"Login"}}},
1399 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1400 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1401 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1402 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1403 }
1404
1405 private:
1406 void doGet(crow::Response &res, const crow::Request &req,
1407 const std::vector<std::string> &params) override
1408 {
1409 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1410 if (params.size() != 1)
1411 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001412 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001413 return;
1414 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001415 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001416 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001417 uint64_t ts = 0;
1418 uint16_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001419 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001420 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001421 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001422 }
1423
1424 sd_journal *journalTmp = nullptr;
1425 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1426 if (ret < 0)
1427 {
1428 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001429 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001430 return;
1431 }
1432 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1433 journalTmp, sd_journal_close);
1434 journalTmp = nullptr;
1435 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001436 // tracking the unique ID
1437 std::string idStr;
1438 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001439 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
1440 for (int i = 0; i <= index; i++)
1441 {
1442 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001443 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1444 {
1445 messages::internalError(asyncResp->res);
1446 return;
1447 }
1448 if (firstEntry)
1449 {
1450 firstEntry = false;
1451 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001452 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001453 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001454 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001455 {
1456 messages::resourceMissingAtURI(asyncResp->res, entryID);
1457 return;
1458 }
1459
1460 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1461 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001462 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001463 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001464 return;
1465 }
1466 }
1467};
1468
Jason M. Bills424c4172019-03-21 13:50:33 -07001469class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001470{
1471 public:
1472 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001473 CrashdumpService(CrowApp &app) :
1474 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001475 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001476 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001477 {boost::beast::http::verb::get, {{"Login"}}},
1478 {boost::beast::http::verb::head, {{"Login"}}},
1479 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1480 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1481 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1482 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001483 }
1484
1485 private:
1486 /**
1487 * Functions triggers appropriate requests on DBus
1488 */
1489 void doGet(crow::Response &res, const crow::Request &req,
1490 const std::vector<std::string> &params) override
1491 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001492 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001493 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001494 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001495 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001496 asyncResp->res.jsonValue["@odata.type"] =
1497 "#LogService.v1_1_0.LogService";
1498 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001499 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Bills424c4172019-03-21 13:50:33 -07001500 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1501 asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1502 asyncResp->res.jsonValue["Id"] = "Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001503 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1504 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001505 asyncResp->res.jsonValue["Entries"] = {
1506 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001507 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001508 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -07001509 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001510 {{"#Crashdump.OnDemand",
1511 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1512 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001513
1514#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001515 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001516 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001517 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1518 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001519#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001520 }
1521};
1522
Jason M. Bills424c4172019-03-21 13:50:33 -07001523class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001524{
1525 public:
1526 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001527 CrashdumpEntryCollection(CrowApp &app) :
1528 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001529 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001530 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001531 {boost::beast::http::verb::get, {{"Login"}}},
1532 {boost::beast::http::verb::head, {{"Login"}}},
1533 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1534 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1535 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1536 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001537 }
1538
1539 private:
1540 /**
1541 * Functions triggers appropriate requests on DBus
1542 */
1543 void doGet(crow::Response &res, const crow::Request &req,
1544 const std::vector<std::string> &params) override
1545 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001546 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001547 // Collections don't include the static data added by SubRoute because
1548 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001549 auto getLogEntriesCallback = [asyncResp](
1550 const boost::system::error_code ec,
1551 const std::vector<std::string> &resp) {
1552 if (ec)
1553 {
1554 if (ec.value() !=
1555 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001556 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001557 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1558 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001559 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001560 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001561 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001562 }
1563 asyncResp->res.jsonValue["@odata.type"] =
1564 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001565 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001566 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001567 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsd53dd412019-02-12 17:16:22 -08001568 "/redfish/v1/"
1569 "$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Bills424c4172019-03-21 13:50:33 -07001570 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001571 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001572 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001573 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1574 logEntryArray = nlohmann::json::array();
1575 for (const std::string &objpath : resp)
1576 {
Jason M. Bills48e46392019-02-13 12:58:37 -08001577 // Don't list the on-demand log
Jason M. Bills424c4172019-03-21 13:50:33 -07001578 if (objpath.compare(CrashdumpOnDemandPath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -07001579 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001580 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -07001581 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001582 std::size_t lastPos = objpath.rfind("/");
1583 if (lastPos != std::string::npos)
1584 {
1585 logEntryArray.push_back(
Jason M. Billsd53dd412019-02-12 17:16:22 -08001586 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
Jason M. Bills424c4172019-03-21 13:50:33 -07001587 "Crashdump/Entries/" +
Jason M. Billse1f26342018-07-18 12:12:00 -07001588 objpath.substr(lastPos + 1)}});
1589 }
1590 }
1591 asyncResp->res.jsonValue["Members@odata.count"] =
1592 logEntryArray.size();
1593 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001594 crow::connections::systemBus->async_method_call(
1595 std::move(getLogEntriesCallback),
1596 "xyz.openbmc_project.ObjectMapper",
1597 "/xyz/openbmc_project/object_mapper",
1598 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills424c4172019-03-21 13:50:33 -07001599 std::array<const char *, 1>{CrashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001600 }
1601};
1602
Jason M. Bills424c4172019-03-21 13:50:33 -07001603std::string getLogCreatedTime(const nlohmann::json &Crashdump)
Ed Tanous1da66f72018-07-27 16:13:37 -07001604{
Jason M. Bills424c4172019-03-21 13:50:33 -07001605 nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1606 if (cdIt != Crashdump.end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001607 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001608 nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1609 if (siIt != cdIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001610 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001611 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1612 if (tsIt != siIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001613 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001614 const std::string *logTime =
1615 tsIt->get_ptr<const std::string *>();
1616 if (logTime != nullptr)
1617 {
1618 return *logTime;
1619 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001620 }
1621 }
1622 }
1623 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
1624
1625 return std::string();
1626}
1627
Jason M. Bills424c4172019-03-21 13:50:33 -07001628class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001629{
1630 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001631 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001632 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001633 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001634 std::string())
1635 {
1636 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001637 {boost::beast::http::verb::get, {{"Login"}}},
1638 {boost::beast::http::verb::head, {{"Login"}}},
1639 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1640 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1641 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1642 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001643 }
1644
1645 private:
1646 void doGet(crow::Response &res, const crow::Request &req,
1647 const std::vector<std::string> &params) override
1648 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001649 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001650 if (params.size() != 1)
1651 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001652 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001653 return;
1654 }
Ed Tanousb01bf292019-03-25 19:25:26 +00001655 const uint8_t logId = std::atoi(params[0].c_str());
Ed Tanousabf2add2019-01-22 16:40:12 -08001656 auto getStoredLogCallback = [asyncResp, logId](
1657 const boost::system::error_code ec,
1658 const std::variant<std::string> &resp) {
1659 if (ec)
1660 {
1661 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1662 messages::internalError(asyncResp->res);
1663 return;
1664 }
1665 const std::string *log = std::get_if<std::string>(&resp);
1666 if (log == nullptr)
1667 {
1668 messages::internalError(asyncResp->res);
1669 return;
1670 }
1671 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1672 if (j.is_discarded())
1673 {
1674 messages::internalError(asyncResp->res);
1675 return;
1676 }
1677 std::string t = getLogCreatedTime(j);
1678 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001679 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001680 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1681 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001682 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
Ed Tanousabf2add2019-01-22 16:40:12 -08001683 std::to_string(logId)},
Jason M. Bills424c4172019-03-21 13:50:33 -07001684 {"Name", "CPU Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001685 {"Id", logId},
1686 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001687 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001688 {"Oem", {{"Intel", std::move(j)}}},
1689 {"Created", std::move(t)}};
1690 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001691 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001692 std::move(getStoredLogCallback), CrashdumpObject,
1693 CrashdumpPath + std::string("/") + std::to_string(logId),
1694 "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1695 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001696 }
1697};
1698
Jason M. Bills424c4172019-03-21 13:50:33 -07001699class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001700{
1701 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001702 OnDemandCrashdump(CrowApp &app) :
1703 Node(app,
1704 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1705 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001706 {
1707 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001708 {boost::beast::http::verb::get, {{"Login"}}},
1709 {boost::beast::http::verb::head, {{"Login"}}},
1710 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1711 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1712 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1713 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001714 }
1715
1716 private:
1717 void doPost(crow::Response &res, const crow::Request &req,
1718 const std::vector<std::string> &params) override
1719 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001720 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001721 static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
Ed Tanous1da66f72018-07-27 16:13:37 -07001722
Jason M. Bills48e46392019-02-13 12:58:37 -08001723 // Only allow one OnDemand Log request at a time
1724 if (onDemandLogMatcher != nullptr)
Ed Tanous1da66f72018-07-27 16:13:37 -07001725 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001726 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -07001727 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -07001728 return;
1729 }
1730 // Make this static so it survives outside this method
1731 static boost::asio::deadline_timer timeout(*req.ioService);
1732
1733 timeout.expires_from_now(boost::posix_time::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -07001734 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Jason M. Bills48e46392019-02-13 12:58:37 -08001735 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001736 if (ec)
1737 {
1738 // operation_aborted is expected if timer is canceled before
1739 // completion.
1740 if (ec != boost::asio::error::operation_aborted)
1741 {
1742 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
1743 }
1744 return;
1745 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001746 BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
Ed Tanous1da66f72018-07-27 16:13:37 -07001747
Jason M. Billsf12894f2018-10-09 12:45:45 -07001748 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001749 });
1750
Jason M. Bills48e46392019-02-13 12:58:37 -08001751 auto onDemandLogMatcherCallback = [asyncResp](
1752 sdbusplus::message::message &m) {
1753 BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
Ed Tanous1da66f72018-07-27 16:13:37 -07001754 boost::system::error_code ec;
1755 timeout.cancel(ec);
1756 if (ec)
1757 {
1758 BMCWEB_LOG_ERROR << "error canceling timer " << ec;
1759 }
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001760 sdbusplus::message::object_path objPath;
Ed Tanous1da66f72018-07-27 16:13:37 -07001761 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -08001762 std::string, boost::container::flat_map<
1763 std::string, std::variant<std::string>>>
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001764 interfacesAdded;
1765 m.read(objPath, interfacesAdded);
Ed Tanousabf2add2019-01-22 16:40:12 -08001766 const std::string *log = std::get_if<std::string>(
Jason M. Bills424c4172019-03-21 13:50:33 -07001767 &interfacesAdded[CrashdumpInterface]["Log"]);
Ed Tanous1da66f72018-07-27 16:13:37 -07001768 if (log == nullptr)
1769 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001770 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001771 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001772 // match object inside which this lambda is executing. Once it
1773 // is set to nullptr, the match object will be destroyed and the
1774 // lambda will lose its context, including res, so it needs to
1775 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001776 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001777 return;
1778 }
1779 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1780 if (j.is_discarded())
1781 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001782 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001783 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001784 // match object inside which this lambda is executing. Once it
1785 // is set to nullptr, the match object will be destroyed and the
1786 // lambda will lose its context, including res, so it needs to
1787 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001788 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001789 return;
1790 }
1791 std::string t = getLogCreatedTime(j);
Jason M. Billse1f26342018-07-18 12:12:00 -07001792 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001793 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001794 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001795 {"Name", "CPU Crashdump"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001796 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001797 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001798 {"Oem", {{"Intel", std::move(j)}}},
1799 {"Created", std::move(t)}};
Jason M. Bills48e46392019-02-13 12:58:37 -08001800 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001801 // match object inside which this lambda is executing. Once it is
1802 // set to nullptr, the match object will be destroyed and the lambda
1803 // will lose its context, including res, so it needs to be the last
1804 // thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001805 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001806 };
Jason M. Bills48e46392019-02-13 12:58:37 -08001807 onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
Ed Tanous1da66f72018-07-27 16:13:37 -07001808 *crow::connections::systemBus,
1809 sdbusplus::bus::match::rules::interfacesAdded() +
Jason M. Bills424c4172019-03-21 13:50:33 -07001810 sdbusplus::bus::match::rules::argNpath(0,
1811 CrashdumpOnDemandPath),
Jason M. Bills48e46392019-02-13 12:58:37 -08001812 std::move(onDemandLogMatcherCallback));
Ed Tanous1da66f72018-07-27 16:13:37 -07001813
Jason M. Bills48e46392019-02-13 12:58:37 -08001814 auto generateonDemandLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -07001815 [asyncResp](const boost::system::error_code ec,
1816 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -07001817 if (ec)
1818 {
1819 if (ec.value() ==
1820 boost::system::errc::operation_not_supported)
1821 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001822 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001823 }
1824 else
1825 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001826 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001827 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001828 boost::system::error_code timeoutec;
1829 timeout.cancel(timeoutec);
1830 if (timeoutec)
1831 {
1832 BMCWEB_LOG_ERROR << "error canceling timer "
1833 << timeoutec;
1834 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001835 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001836 return;
1837 }
1838 };
1839 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001840 std::move(generateonDemandLogCallback), CrashdumpObject,
1841 CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001842 }
1843};
1844
Jason M. Billse1f26342018-07-18 12:12:00 -07001845class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001846{
1847 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001848 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001849 Node(app,
1850 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1851 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001852 {
1853 entityPrivileges = {
1854 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1855 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1856 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1857 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1858 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1859 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1860 }
1861
1862 private:
1863 void doPost(crow::Response &res, const crow::Request &req,
1864 const std::vector<std::string> &params) override
1865 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001866 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousb1556422018-10-16 14:09:17 -07001867 uint8_t clientAddress = 0;
1868 uint8_t readLength = 0;
Ed Tanous1da66f72018-07-27 16:13:37 -07001869 std::vector<uint8_t> peciCommand;
Ed Tanousb1556422018-10-16 14:09:17 -07001870 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1871 "ReadLength", readLength, "PECICommand",
1872 peciCommand))
Ed Tanous1da66f72018-07-27 16:13:37 -07001873 {
Ed Tanousb1556422018-10-16 14:09:17 -07001874 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001875 }
Ed Tanousb1556422018-10-16 14:09:17 -07001876
Ed Tanous1da66f72018-07-27 16:13:37 -07001877 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001878 auto sendRawPECICallback =
1879 [asyncResp](const boost::system::error_code ec,
1880 const std::vector<uint8_t> &resp) {
1881 if (ec)
1882 {
1883 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
1884 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001885 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001886 return;
1887 }
1888 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1889 {"PECIResponse", resp}};
1890 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001891 // Call the SendRawPECI command with the provided data
1892 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001893 std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1894 CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001895 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -07001896 }
1897};
1898
Andrew Geisslercb92c032018-08-17 07:56:14 -07001899/**
1900 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1901 */
1902class DBusLogServiceActionsClear : public Node
1903{
1904 public:
1905 DBusLogServiceActionsClear(CrowApp &app) :
1906 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1907 "LogService.Reset")
1908 {
1909 entityPrivileges = {
1910 {boost::beast::http::verb::get, {{"Login"}}},
1911 {boost::beast::http::verb::head, {{"Login"}}},
1912 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1913 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1914 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1915 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1916 }
1917
1918 private:
1919 /**
1920 * Function handles POST method request.
1921 * The Clear Log actions does not require any parameter.The action deletes
1922 * all entries found in the Entries collection for this Log Service.
1923 */
1924 void doPost(crow::Response &res, const crow::Request &req,
1925 const std::vector<std::string> &params) override
1926 {
1927 BMCWEB_LOG_DEBUG << "Do delete all entries.";
1928
1929 auto asyncResp = std::make_shared<AsyncResp>(res);
1930 // Process response from Logging service.
1931 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1932 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1933 if (ec)
1934 {
1935 // TODO Handle for specific error code
1936 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1937 asyncResp->res.result(
1938 boost::beast::http::status::internal_server_error);
1939 return;
1940 }
1941
1942 asyncResp->res.result(boost::beast::http::status::no_content);
1943 };
1944
1945 // Make call to Logging service to request Clear Log
1946 crow::connections::systemBus->async_method_call(
1947 resp_handler, "xyz.openbmc_project.Logging",
1948 "/xyz/openbmc_project/logging",
1949 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1950 }
1951};
Ed Tanous1da66f72018-07-27 16:13:37 -07001952} // namespace redfish