blob: 7a631622315c27b21781c86d3943f5c500dba366 [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 Tanous271584a2019-07-09 16:24:22 -0700148 ret = sd_journal_get_data(journal, field.data(),
149 reinterpret_cast<const void **>(&data), &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,
Ed Tanous271584a2019-07-09 16:24:22 -0700162 long int &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700163{
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,
Ed Tanous271584a2019-07-09 16:24:22 -0700208 uint64_t &skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700209{
210 char *skipParam = req.urlParams.get("$skip");
211 if (skipParam != nullptr)
212 {
213 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700214 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700215 if (*skipParam == '\0' || *ptr != '\0')
216 {
217
218 messages::queryParameterValueTypeError(res, std::string(skipParam),
219 "$skip");
220 return false;
221 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700222 }
223 return true;
224}
225
Ed Tanous271584a2019-07-09 16:24:22 -0700226static constexpr const uint64_t maxEntriesPerPage = 1000;
Jason M. Bills16428a12018-11-02 12:42:29 -0700227static bool getTopParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700228 uint64_t &top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700229{
230 char *topParam = req.urlParams.get("$top");
231 if (topParam != nullptr)
232 {
233 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700234 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700235 if (*topParam == '\0' || *ptr != '\0')
236 {
237 messages::queryParameterValueTypeError(res, std::string(topParam),
238 "$top");
239 return false;
240 }
Ed Tanous271584a2019-07-09 16:24:22 -0700241 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700242 {
243
244 messages::queryParameterOutOfRange(
245 res, std::to_string(top), "$top",
246 "1-" + std::to_string(maxEntriesPerPage));
247 return false;
248 }
249 }
250 return true;
251}
252
Jason M. Billse85d6b12019-07-29 17:01:15 -0700253static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
254 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700255{
256 int ret = 0;
257 static uint64_t prevTs = 0;
258 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700259 if (firstEntry)
260 {
261 prevTs = 0;
262 }
263
Jason M. Bills16428a12018-11-02 12:42:29 -0700264 // Get the entry timestamp
265 uint64_t curTs = 0;
266 ret = sd_journal_get_realtime_usec(journal, &curTs);
267 if (ret < 0)
268 {
269 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
270 << strerror(-ret);
271 return false;
272 }
273 // If the timestamp isn't unique, increment the index
274 if (curTs == prevTs)
275 {
276 index++;
277 }
278 else
279 {
280 // Otherwise, reset it
281 index = 0;
282 }
283 // Save the timestamp
284 prevTs = curTs;
285
286 entryID = std::to_string(curTs);
287 if (index > 0)
288 {
289 entryID += "_" + std::to_string(index);
290 }
291 return true;
292}
293
Jason M. Billse85d6b12019-07-29 17:01:15 -0700294static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
295 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700296{
Ed Tanous271584a2019-07-09 16:24:22 -0700297 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700298 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700299 if (firstEntry)
300 {
301 prevTs = 0;
302 }
303
Jason M. Bills95820182019-04-22 16:25:34 -0700304 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700305 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700306 std::tm timeStruct = {};
307 std::istringstream entryStream(logEntry);
308 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
309 {
310 curTs = std::mktime(&timeStruct);
311 }
312 // If the timestamp isn't unique, increment the index
313 if (curTs == prevTs)
314 {
315 index++;
316 }
317 else
318 {
319 // Otherwise, reset it
320 index = 0;
321 }
322 // Save the timestamp
323 prevTs = curTs;
324
325 entryID = std::to_string(curTs);
326 if (index > 0)
327 {
328 entryID += "_" + std::to_string(index);
329 }
330 return true;
331}
332
Jason M. Bills16428a12018-11-02 12:42:29 -0700333static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
Ed Tanous271584a2019-07-09 16:24:22 -0700334 uint64_t &timestamp, uint64_t &index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700335{
336 if (entryID.empty())
337 {
338 return false;
339 }
340 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800341 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700342
343 auto underscorePos = tsStr.find("_");
344 if (underscorePos != tsStr.npos)
345 {
346 // Timestamp has an index
347 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800348 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700349 indexStr.remove_prefix(underscorePos + 1);
350 std::size_t pos;
351 try
352 {
Ed Tanous39e77502019-03-04 17:35:53 -0800353 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700354 }
Ed Tanous271584a2019-07-09 16:24:22 -0700355 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700356 {
357 messages::resourceMissingAtURI(res, entryID);
358 return false;
359 }
Ed Tanous271584a2019-07-09 16:24:22 -0700360 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700361 {
362 messages::resourceMissingAtURI(res, entryID);
363 return false;
364 }
365 if (pos != indexStr.size())
366 {
367 messages::resourceMissingAtURI(res, entryID);
368 return false;
369 }
370 }
371 // Timestamp has no index
372 std::size_t pos;
373 try
374 {
Ed Tanous39e77502019-03-04 17:35:53 -0800375 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700376 }
Ed Tanous271584a2019-07-09 16:24:22 -0700377 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700378 {
379 messages::resourceMissingAtURI(res, entryID);
380 return false;
381 }
Ed Tanous271584a2019-07-09 16:24:22 -0700382 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700383 {
384 messages::resourceMissingAtURI(res, entryID);
385 return false;
386 }
387 if (pos != tsStr.size())
388 {
389 messages::resourceMissingAtURI(res, entryID);
390 return false;
391 }
392 return true;
393}
394
Jason M. Bills95820182019-04-22 16:25:34 -0700395static bool
396 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
397{
398 static const std::filesystem::path redfishLogDir = "/var/log";
399 static const std::string redfishLogFilename = "redfish";
400
401 // Loop through the directory looking for redfish log files
402 for (const std::filesystem::directory_entry &dirEnt :
403 std::filesystem::directory_iterator(redfishLogDir))
404 {
405 // If we find a redfish log file, save the path
406 std::string filename = dirEnt.path().filename();
407 if (boost::starts_with(filename, redfishLogFilename))
408 {
409 redfishLogFiles.emplace_back(redfishLogDir / filename);
410 }
411 }
412 // As the log files rotate, they are appended with a ".#" that is higher for
413 // the older logs. Since we don't expect more than 10 log files, we
414 // can just sort the list to get them in order from newest to oldest
415 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
416
417 return !redfishLogFiles.empty();
418}
419
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800420class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700421{
422 public:
423 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800424 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800425 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800426 {
427 entityPrivileges = {
428 {boost::beast::http::verb::get, {{"Login"}}},
429 {boost::beast::http::verb::head, {{"Login"}}},
430 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
431 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
432 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
433 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
434 }
435
436 private:
437 /**
438 * Functions triggers appropriate requests on DBus
439 */
440 void doGet(crow::Response &res, const crow::Request &req,
441 const std::vector<std::string> &params) override
442 {
443 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800444 // Collections don't include the static data added by SubRoute because
445 // it has a duplicate entry for members
446 asyncResp->res.jsonValue["@odata.type"] =
447 "#LogServiceCollection.LogServiceCollection";
448 asyncResp->res.jsonValue["@odata.context"] =
449 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
450 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800451 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800452 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
453 asyncResp->res.jsonValue["Description"] =
454 "Collection of LogServices for this Computer System";
455 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
456 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800457 logServiceArray.push_back(
458 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800459#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
460 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500461 {{"@odata.id",
462 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800463#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800464 asyncResp->res.jsonValue["Members@odata.count"] =
465 logServiceArray.size();
466 }
467};
468
469class EventLogService : public Node
470{
471 public:
472 template <typename CrowApp>
473 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800474 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800475 {
476 entityPrivileges = {
477 {boost::beast::http::verb::get, {{"Login"}}},
478 {boost::beast::http::verb::head, {{"Login"}}},
479 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
480 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
481 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
482 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
483 }
484
485 private:
486 void doGet(crow::Response &res, const crow::Request &req,
487 const std::vector<std::string> &params) override
488 {
489 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
490
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800491 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800492 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800493 asyncResp->res.jsonValue["@odata.type"] =
494 "#LogService.v1_1_0.LogService";
495 asyncResp->res.jsonValue["@odata.context"] =
496 "/redfish/v1/$metadata#LogService.LogService";
497 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
511class EventLogClear : public Node
512{
513 public:
514 EventLogClear(CrowApp &app) :
515 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"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800645 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800646 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700647 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700648 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800649 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700650 {"Id", logEntryID},
651 {"Message", std::move(msg)},
652 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800653 {"MessageArgs", std::move(messageArgs)},
654 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700655 {"Severity", std::move(severity)},
656 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800657 return 0;
658}
659
Anthony Wilson27062602019-04-22 02:10:09 -0500660class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800661{
662 public:
663 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500664 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800665 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800666 {
667 entityPrivileges = {
668 {boost::beast::http::verb::get, {{"Login"}}},
669 {boost::beast::http::verb::head, {{"Login"}}},
670 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
671 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
672 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
673 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
674 }
675
676 private:
677 void doGet(crow::Response &res, const crow::Request &req,
678 const std::vector<std::string> &params) override
679 {
680 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700681 uint64_t skip = 0;
682 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800683 if (!getSkipParam(asyncResp->res, req, skip))
684 {
685 return;
686 }
687 if (!getTopParam(asyncResp->res, req, top))
688 {
689 return;
690 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800691 // Collections don't include the static data added by SubRoute because
692 // it has a duplicate entry for members
693 asyncResp->res.jsonValue["@odata.type"] =
694 "#LogEntryCollection.LogEntryCollection";
695 asyncResp->res.jsonValue["@odata.context"] =
696 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
697 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800698 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800699 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
700 asyncResp->res.jsonValue["Description"] =
701 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700702
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800703 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
704 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700705 // Go through the log files and create a unique ID for each entry
706 std::vector<std::filesystem::path> redfishLogFiles;
707 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000708 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700709 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700710
711 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700712 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
713 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800714 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700715 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700716 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800717 {
718 continue;
719 }
720
Jason M. Billse85d6b12019-07-29 17:01:15 -0700721 // Reset the unique ID on the first entry
722 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700723 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800724 {
Jason M. Bills95820182019-04-22 16:25:34 -0700725 entryCount++;
726 // Handle paging using skip (number of entries to skip from the
727 // start) and top (number of entries to display)
728 if (entryCount <= skip || entryCount > skip + top)
729 {
730 continue;
731 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800732
Jason M. Bills95820182019-04-22 16:25:34 -0700733 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700734 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700735 {
736 continue;
737 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800738
Jason M. Billse85d6b12019-07-29 17:01:15 -0700739 if (firstEntry)
740 {
741 firstEntry = false;
742 }
743
Jason M. Bills95820182019-04-22 16:25:34 -0700744 logEntryArray.push_back({});
745 nlohmann::json &bmcLogEntry = logEntryArray.back();
746 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
747 {
748 messages::internalError(asyncResp->res);
749 return;
750 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800751 }
752 }
753 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
754 if (skip + top < entryCount)
755 {
756 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700757 "/redfish/v1/Systems/system/LogServices/EventLog/"
758 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800759 std::to_string(skip + top);
760 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500761 }
762};
763
Jason M. Bills897967d2019-07-29 17:05:30 -0700764class JournalEventLogEntry : public Node
765{
766 public:
767 JournalEventLogEntry(CrowApp &app) :
768 Node(app,
769 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
770 std::string())
771 {
772 entityPrivileges = {
773 {boost::beast::http::verb::get, {{"Login"}}},
774 {boost::beast::http::verb::head, {{"Login"}}},
775 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
776 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
777 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
778 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
779 }
780
781 private:
782 void doGet(crow::Response &res, const crow::Request &req,
783 const std::vector<std::string> &params) override
784 {
785 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
786 if (params.size() != 1)
787 {
788 messages::internalError(asyncResp->res);
789 return;
790 }
791 const std::string &targetID = params[0];
792
793 // Go through the log files and check the unique ID for each entry to
794 // find the target entry
795 std::vector<std::filesystem::path> redfishLogFiles;
796 getRedfishLogFiles(redfishLogFiles);
797 std::string logEntry;
798
799 // Oldest logs are in the last file, so start there and loop backwards
800 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
801 it++)
802 {
803 std::ifstream logStream(*it);
804 if (!logStream.is_open())
805 {
806 continue;
807 }
808
809 // Reset the unique ID on the first entry
810 bool firstEntry = true;
811 while (std::getline(logStream, logEntry))
812 {
813 std::string idStr;
814 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
815 {
816 continue;
817 }
818
819 if (firstEntry)
820 {
821 firstEntry = false;
822 }
823
824 if (idStr == targetID)
825 {
826 if (fillEventLogEntryJson(idStr, logEntry,
827 asyncResp->res.jsonValue) != 0)
828 {
829 messages::internalError(asyncResp->res);
830 return;
831 }
832 return;
833 }
834 }
835 }
836 // Requested ID was not found
837 messages::resourceMissingAtURI(asyncResp->res, targetID);
838 }
839};
840
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500841class DBusEventLogEntryCollection : public Node
842{
843 public:
844 template <typename CrowApp>
845 DBusEventLogEntryCollection(CrowApp &app) :
846 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
847 {
848 entityPrivileges = {
849 {boost::beast::http::verb::get, {{"Login"}}},
850 {boost::beast::http::verb::head, {{"Login"}}},
851 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
852 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
853 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
854 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
855 }
856
857 private:
858 void doGet(crow::Response &res, const crow::Request &req,
859 const std::vector<std::string> &params) override
860 {
861 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
862
863 // Collections don't include the static data added by SubRoute because
864 // it has a duplicate entry for members
865 asyncResp->res.jsonValue["@odata.type"] =
866 "#LogEntryCollection.LogEntryCollection";
867 asyncResp->res.jsonValue["@odata.context"] =
868 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
869 asyncResp->res.jsonValue["@odata.id"] =
870 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
871 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
872 asyncResp->res.jsonValue["Description"] =
873 "Collection of System Event Log Entries";
874
Andrew Geisslercb92c032018-08-17 07:56:14 -0700875 // DBus implementation of EventLog/Entries
876 // Make call to Logging Service to find all log entry objects
877 crow::connections::systemBus->async_method_call(
878 [asyncResp](const boost::system::error_code ec,
879 GetManagedObjectsType &resp) {
880 if (ec)
881 {
882 // TODO Handle for specific error code
883 BMCWEB_LOG_ERROR
884 << "getLogEntriesIfaceData resp_handler got error "
885 << ec;
886 messages::internalError(asyncResp->res);
887 return;
888 }
889 nlohmann::json &entriesArray =
890 asyncResp->res.jsonValue["Members"];
891 entriesArray = nlohmann::json::array();
892 for (auto &objectPath : resp)
893 {
894 for (auto &interfaceMap : objectPath.second)
895 {
896 if (interfaceMap.first !=
897 "xyz.openbmc_project.Logging.Entry")
898 {
899 BMCWEB_LOG_DEBUG << "Bailing early on "
900 << interfaceMap.first;
901 continue;
902 }
903 entriesArray.push_back({});
904 nlohmann::json &thisEntry = entriesArray.back();
905 uint32_t *id;
906 std::time_t timestamp;
907 std::string *severity, *message;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700908 for (auto &propertyMap : interfaceMap.second)
909 {
910 if (propertyMap.first == "Id")
911 {
912 id = sdbusplus::message::variant_ns::get_if<
913 uint32_t>(&propertyMap.second);
914 if (id == nullptr)
915 {
916 messages::propertyMissing(asyncResp->res,
917 "Id");
918 }
919 }
920 else if (propertyMap.first == "Timestamp")
921 {
922 const uint64_t *millisTimeStamp =
923 std::get_if<uint64_t>(&propertyMap.second);
924 if (millisTimeStamp == nullptr)
925 {
926 messages::propertyMissing(asyncResp->res,
927 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -0700928 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700929 }
930 // Retrieve Created property with format:
931 // yyyy-mm-ddThh:mm:ss
932 std::chrono::milliseconds chronoTimeStamp(
933 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -0700934 timestamp = std::chrono::duration_cast<
935 std::chrono::duration<int>>(
936 chronoTimeStamp)
937 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -0700938 }
939 else if (propertyMap.first == "Severity")
940 {
941 severity = std::get_if<std::string>(
942 &propertyMap.second);
943 if (severity == nullptr)
944 {
945 messages::propertyMissing(asyncResp->res,
946 "Severity");
947 }
948 }
949 else if (propertyMap.first == "Message")
950 {
951 message = std::get_if<std::string>(
952 &propertyMap.second);
953 if (message == nullptr)
954 {
955 messages::propertyMissing(asyncResp->res,
956 "Message");
957 }
958 }
959 }
960 thisEntry = {
961 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
962 {"@odata.context", "/redfish/v1/"
963 "$metadata#LogEntry.LogEntry"},
964 {"@odata.id",
965 "/redfish/v1/Systems/system/LogServices/EventLog/"
966 "Entries/" +
967 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -0500968 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700969 {"Id", std::to_string(*id)},
970 {"Message", *message},
971 {"EntryType", "Event"},
972 {"Severity",
973 translateSeverityDbusToRedfish(*severity)},
974 {"Created", crow::utility::getDateTime(timestamp)}};
975 }
976 }
977 std::sort(entriesArray.begin(), entriesArray.end(),
978 [](const nlohmann::json &left,
979 const nlohmann::json &right) {
980 return (left["Id"] <= right["Id"]);
981 });
982 asyncResp->res.jsonValue["Members@odata.count"] =
983 entriesArray.size();
984 },
985 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
986 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800987 }
988};
989
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500990class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800991{
992 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500993 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800994 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -0800995 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
996 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800997 {
998 entityPrivileges = {
999 {boost::beast::http::verb::get, {{"Login"}}},
1000 {boost::beast::http::verb::head, {{"Login"}}},
1001 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1002 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1003 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1004 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1005 }
1006
1007 private:
1008 void doGet(crow::Response &res, const crow::Request &req,
1009 const std::vector<std::string> &params) override
1010 {
1011 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001012 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001013 {
1014 messages::internalError(asyncResp->res);
1015 return;
1016 }
Ed Tanous029573d2019-02-01 10:57:49 -08001017 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001018
Andrew Geisslercb92c032018-08-17 07:56:14 -07001019 // DBus implementation of EventLog/Entries
1020 // Make call to Logging Service to find all log entry objects
1021 crow::connections::systemBus->async_method_call(
1022 [asyncResp, entryID](const boost::system::error_code ec,
1023 GetManagedPropertyType &resp) {
1024 if (ec)
1025 {
1026 BMCWEB_LOG_ERROR
1027 << "EventLogEntry (DBus) resp_handler got error " << ec;
1028 messages::internalError(asyncResp->res);
1029 return;
1030 }
1031 uint32_t *id;
1032 std::time_t timestamp;
1033 std::string *severity, *message;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001034 for (auto &propertyMap : resp)
1035 {
1036 if (propertyMap.first == "Id")
1037 {
1038 id = std::get_if<uint32_t>(&propertyMap.second);
1039 if (id == nullptr)
1040 {
1041 messages::propertyMissing(asyncResp->res, "Id");
1042 }
1043 }
1044 else if (propertyMap.first == "Timestamp")
1045 {
1046 const uint64_t *millisTimeStamp =
1047 std::get_if<uint64_t>(&propertyMap.second);
1048 if (millisTimeStamp == nullptr)
1049 {
1050 messages::propertyMissing(asyncResp->res,
1051 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001052 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001053 }
1054 // Retrieve Created property with format:
1055 // yyyy-mm-ddThh:mm:ss
1056 std::chrono::milliseconds chronoTimeStamp(
1057 *millisTimeStamp);
1058 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001059 std::chrono::duration_cast<
1060 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001061 .count();
1062 }
1063 else if (propertyMap.first == "Severity")
1064 {
1065 severity =
1066 std::get_if<std::string>(&propertyMap.second);
1067 if (severity == nullptr)
1068 {
1069 messages::propertyMissing(asyncResp->res,
1070 "Severity");
1071 }
1072 }
1073 else if (propertyMap.first == "Message")
1074 {
1075 message = std::get_if<std::string>(&propertyMap.second);
1076 if (message == nullptr)
1077 {
1078 messages::propertyMissing(asyncResp->res,
1079 "Message");
1080 }
1081 }
1082 }
Ed Tanous271584a2019-07-09 16:24:22 -07001083 if (id == nullptr || message == nullptr || severity == nullptr)
1084 {
1085 return;
1086 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001087 asyncResp->res.jsonValue = {
1088 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1089 {"@odata.context", "/redfish/v1/"
1090 "$metadata#LogEntry.LogEntry"},
1091 {"@odata.id",
1092 "/redfish/v1/Systems/system/LogServices/EventLog/"
1093 "Entries/" +
1094 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001095 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001096 {"Id", std::to_string(*id)},
1097 {"Message", *message},
1098 {"EntryType", "Event"},
1099 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001100 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001101 },
1102 "xyz.openbmc_project.Logging",
1103 "/xyz/openbmc_project/logging/entry/" + entryID,
1104 "org.freedesktop.DBus.Properties", "GetAll",
1105 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001106 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001107
1108 void doDelete(crow::Response &res, const crow::Request &req,
1109 const std::vector<std::string> &params) override
1110 {
1111
1112 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1113
1114 auto asyncResp = std::make_shared<AsyncResp>(res);
1115
1116 if (params.size() != 1)
1117 {
1118 messages::internalError(asyncResp->res);
1119 return;
1120 }
1121 std::string entryID = params[0];
1122
1123 dbus::utility::escapePathForDbus(entryID);
1124
1125 // Process response from Logging service.
1126 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1127 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1128 if (ec)
1129 {
1130 // TODO Handle for specific error code
1131 BMCWEB_LOG_ERROR
1132 << "EventLogEntry (DBus) doDelete respHandler got error "
1133 << ec;
1134 asyncResp->res.result(
1135 boost::beast::http::status::internal_server_error);
1136 return;
1137 }
1138
1139 asyncResp->res.result(boost::beast::http::status::ok);
1140 };
1141
1142 // Make call to Logging service to request Delete Log
1143 crow::connections::systemBus->async_method_call(
1144 respHandler, "xyz.openbmc_project.Logging",
1145 "/xyz/openbmc_project/logging/entry/" + entryID,
1146 "xyz.openbmc_project.Object.Delete", "Delete");
1147 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001148};
1149
1150class BMCLogServiceCollection : public Node
1151{
1152 public:
1153 template <typename CrowApp>
1154 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001155 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001156 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001157 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001158 {boost::beast::http::verb::get, {{"Login"}}},
1159 {boost::beast::http::verb::head, {{"Login"}}},
1160 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1161 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1162 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1163 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001164 }
1165
1166 private:
1167 /**
1168 * Functions triggers appropriate requests on DBus
1169 */
1170 void doGet(crow::Response &res, const crow::Request &req,
1171 const std::vector<std::string> &params) override
1172 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001173 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001174 // Collections don't include the static data added by SubRoute because
1175 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001176 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001177 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001178 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001179 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001180 asyncResp->res.jsonValue["@odata.id"] =
1181 "/redfish/v1/Managers/bmc/LogServices";
1182 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1183 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001184 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001185 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1186 logServiceArray = nlohmann::json::array();
1187#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1188 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001189 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001190#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001191 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001192 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001193 }
1194};
1195
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001196class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001197{
1198 public:
1199 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001200 BMCJournalLogService(CrowApp &app) :
1201 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001202 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001203 entityPrivileges = {
1204 {boost::beast::http::verb::get, {{"Login"}}},
1205 {boost::beast::http::verb::head, {{"Login"}}},
1206 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1207 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1208 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1209 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1210 }
1211
1212 private:
1213 void doGet(crow::Response &res, const crow::Request &req,
1214 const std::vector<std::string> &params) override
1215 {
1216 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001217 asyncResp->res.jsonValue["@odata.type"] =
1218 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001219 asyncResp->res.jsonValue["@odata.id"] =
1220 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001221 asyncResp->res.jsonValue["@odata.context"] =
1222 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001223 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1224 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1225 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001226 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001227 asyncResp->res.jsonValue["Entries"] = {
1228 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001229 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001230 }
1231};
1232
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001233static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1234 sd_journal *journal,
1235 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001236{
1237 // Get the Log Entry contents
1238 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001239
Ed Tanous39e77502019-03-04 17:35:53 -08001240 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001241 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001242 if (ret < 0)
1243 {
1244 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1245 return 1;
1246 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001247
1248 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001249 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001250 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001251 if (ret < 0)
1252 {
1253 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001254 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001255
1256 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001257 std::string entryTimeStr;
1258 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001259 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001260 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001261 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001262
1263 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001264 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001265 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001266 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001267 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1268 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001269 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001270 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001271 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001272 {"EntryType", "Oem"},
1273 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001274 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001275 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001276 {"Created", std::move(entryTimeStr)}};
1277 return 0;
1278}
1279
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001280class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001281{
1282 public:
1283 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001284 BMCJournalLogEntryCollection(CrowApp &app) :
1285 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001286 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001287 entityPrivileges = {
1288 {boost::beast::http::verb::get, {{"Login"}}},
1289 {boost::beast::http::verb::head, {{"Login"}}},
1290 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1291 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1292 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1293 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1294 }
1295
1296 private:
1297 void doGet(crow::Response &res, const crow::Request &req,
1298 const std::vector<std::string> &params) override
1299 {
1300 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001301 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001302 uint64_t skip = 0;
1303 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001304 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001305 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001306 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001307 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001308 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001309 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001310 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001311 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001312 // Collections don't include the static data added by SubRoute because
1313 // it has a duplicate entry for members
1314 asyncResp->res.jsonValue["@odata.type"] =
1315 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001316 asyncResp->res.jsonValue["@odata.id"] =
1317 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001318 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001319 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001320 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001321 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001322 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1323 asyncResp->res.jsonValue["Description"] =
1324 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001325 asyncResp->res.jsonValue["@odata.id"] =
1326 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001327 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1328 logEntryArray = nlohmann::json::array();
1329
1330 // Go through the journal and use the timestamp to create a unique ID
1331 // for each entry
1332 sd_journal *journalTmp = nullptr;
1333 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1334 if (ret < 0)
1335 {
1336 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001337 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001338 return;
1339 }
1340 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1341 journalTmp, sd_journal_close);
1342 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001343 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001344 // Reset the unique ID on the first entry
1345 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001346 SD_JOURNAL_FOREACH(journal.get())
1347 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001348 entryCount++;
1349 // Handle paging using skip (number of entries to skip from the
1350 // start) and top (number of entries to display)
1351 if (entryCount <= skip || entryCount > skip + top)
1352 {
1353 continue;
1354 }
1355
Jason M. Bills16428a12018-11-02 12:42:29 -07001356 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001357 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001358 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001359 continue;
1360 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001361
Jason M. Billse85d6b12019-07-29 17:01:15 -07001362 if (firstEntry)
1363 {
1364 firstEntry = false;
1365 }
1366
Jason M. Billse1f26342018-07-18 12:12:00 -07001367 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001368 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1369 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1370 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001371 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001372 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001373 return;
1374 }
1375 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001376 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1377 if (skip + top < entryCount)
1378 {
1379 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001380 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001381 std::to_string(skip + top);
1382 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001383 }
1384};
1385
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001386class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001387{
1388 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001389 BMCJournalLogEntry(CrowApp &app) :
1390 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001391 std::string())
1392 {
1393 entityPrivileges = {
1394 {boost::beast::http::verb::get, {{"Login"}}},
1395 {boost::beast::http::verb::head, {{"Login"}}},
1396 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1397 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1398 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1399 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1400 }
1401
1402 private:
1403 void doGet(crow::Response &res, const crow::Request &req,
1404 const std::vector<std::string> &params) override
1405 {
1406 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1407 if (params.size() != 1)
1408 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001409 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001410 return;
1411 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001412 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001413 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001414 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001415 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001416 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001417 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001418 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001419 }
1420
1421 sd_journal *journalTmp = nullptr;
1422 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1423 if (ret < 0)
1424 {
1425 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001426 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001427 return;
1428 }
1429 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1430 journalTmp, sd_journal_close);
1431 journalTmp = nullptr;
1432 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001433 // tracking the unique ID
1434 std::string idStr;
1435 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001436 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001437 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001438 {
1439 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001440 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1441 {
1442 messages::internalError(asyncResp->res);
1443 return;
1444 }
1445 if (firstEntry)
1446 {
1447 firstEntry = false;
1448 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001449 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001450 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001451 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001452 {
1453 messages::resourceMissingAtURI(asyncResp->res, entryID);
1454 return;
1455 }
1456
1457 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1458 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001459 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001460 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001461 return;
1462 }
1463 }
1464};
1465
Jason M. Bills424c4172019-03-21 13:50:33 -07001466class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001467{
1468 public:
1469 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001470 CrashdumpService(CrowApp &app) :
1471 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001472 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001473 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001474 {boost::beast::http::verb::get, {{"Login"}}},
1475 {boost::beast::http::verb::head, {{"Login"}}},
1476 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1477 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1478 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1479 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001480 }
1481
1482 private:
1483 /**
1484 * Functions triggers appropriate requests on DBus
1485 */
1486 void doGet(crow::Response &res, const crow::Request &req,
1487 const std::vector<std::string> &params) override
1488 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001489 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001490 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001491 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001492 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001493 asyncResp->res.jsonValue["@odata.type"] =
1494 "#LogService.v1_1_0.LogService";
1495 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001496 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Bills424c4172019-03-21 13:50:33 -07001497 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1498 asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1499 asyncResp->res.jsonValue["Id"] = "Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001500 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1501 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001502 asyncResp->res.jsonValue["Entries"] = {
1503 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001504 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001505 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -07001506 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001507 {{"#Crashdump.OnDemand",
1508 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1509 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001510
1511#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001512 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001513 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001514 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1515 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001516#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001517 }
1518};
1519
Jason M. Bills424c4172019-03-21 13:50:33 -07001520class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001521{
1522 public:
1523 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001524 CrashdumpEntryCollection(CrowApp &app) :
1525 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001526 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001527 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001528 {boost::beast::http::verb::get, {{"Login"}}},
1529 {boost::beast::http::verb::head, {{"Login"}}},
1530 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1531 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1532 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1533 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001534 }
1535
1536 private:
1537 /**
1538 * Functions triggers appropriate requests on DBus
1539 */
1540 void doGet(crow::Response &res, const crow::Request &req,
1541 const std::vector<std::string> &params) override
1542 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001543 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001544 // Collections don't include the static data added by SubRoute because
1545 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001546 auto getLogEntriesCallback = [asyncResp](
1547 const boost::system::error_code ec,
1548 const std::vector<std::string> &resp) {
1549 if (ec)
1550 {
1551 if (ec.value() !=
1552 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001553 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001554 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1555 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001556 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001557 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001558 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001559 }
1560 asyncResp->res.jsonValue["@odata.type"] =
1561 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001562 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001563 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001564 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsd53dd412019-02-12 17:16:22 -08001565 "/redfish/v1/"
1566 "$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Bills424c4172019-03-21 13:50:33 -07001567 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001568 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001569 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001570 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1571 logEntryArray = nlohmann::json::array();
1572 for (const std::string &objpath : resp)
1573 {
Jason M. Bills48e46392019-02-13 12:58:37 -08001574 // Don't list the on-demand log
Jason M. Bills424c4172019-03-21 13:50:33 -07001575 if (objpath.compare(CrashdumpOnDemandPath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -07001576 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001577 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -07001578 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001579 std::size_t lastPos = objpath.rfind("/");
1580 if (lastPos != std::string::npos)
1581 {
1582 logEntryArray.push_back(
Jason M. Billsd53dd412019-02-12 17:16:22 -08001583 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
Jason M. Bills424c4172019-03-21 13:50:33 -07001584 "Crashdump/Entries/" +
Jason M. Billse1f26342018-07-18 12:12:00 -07001585 objpath.substr(lastPos + 1)}});
1586 }
1587 }
1588 asyncResp->res.jsonValue["Members@odata.count"] =
1589 logEntryArray.size();
1590 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001591 crow::connections::systemBus->async_method_call(
1592 std::move(getLogEntriesCallback),
1593 "xyz.openbmc_project.ObjectMapper",
1594 "/xyz/openbmc_project/object_mapper",
1595 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills424c4172019-03-21 13:50:33 -07001596 std::array<const char *, 1>{CrashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001597 }
1598};
1599
Jason M. Bills424c4172019-03-21 13:50:33 -07001600std::string getLogCreatedTime(const nlohmann::json &Crashdump)
Ed Tanous1da66f72018-07-27 16:13:37 -07001601{
Jason M. Bills424c4172019-03-21 13:50:33 -07001602 nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1603 if (cdIt != Crashdump.end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001604 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001605 nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1606 if (siIt != cdIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001607 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001608 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1609 if (tsIt != siIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001610 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001611 const std::string *logTime =
1612 tsIt->get_ptr<const std::string *>();
1613 if (logTime != nullptr)
1614 {
1615 return *logTime;
1616 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001617 }
1618 }
1619 }
1620 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
1621
1622 return std::string();
1623}
1624
Jason M. Bills424c4172019-03-21 13:50:33 -07001625class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001626{
1627 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001628 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001629 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001630 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001631 std::string())
1632 {
1633 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001634 {boost::beast::http::verb::get, {{"Login"}}},
1635 {boost::beast::http::verb::head, {{"Login"}}},
1636 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1637 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1638 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1639 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001640 }
1641
1642 private:
1643 void doGet(crow::Response &res, const crow::Request &req,
1644 const std::vector<std::string> &params) override
1645 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001646 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001647 if (params.size() != 1)
1648 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001649 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001650 return;
1651 }
Ed Tanous271584a2019-07-09 16:24:22 -07001652 const int logId = std::atoi(params[0].c_str());
Ed Tanousabf2add2019-01-22 16:40:12 -08001653 auto getStoredLogCallback = [asyncResp, logId](
1654 const boost::system::error_code ec,
1655 const std::variant<std::string> &resp) {
1656 if (ec)
1657 {
1658 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1659 messages::internalError(asyncResp->res);
1660 return;
1661 }
1662 const std::string *log = std::get_if<std::string>(&resp);
1663 if (log == nullptr)
1664 {
1665 messages::internalError(asyncResp->res);
1666 return;
1667 }
1668 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1669 if (j.is_discarded())
1670 {
1671 messages::internalError(asyncResp->res);
1672 return;
1673 }
1674 std::string t = getLogCreatedTime(j);
1675 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001676 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001677 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1678 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001679 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
Ed Tanousabf2add2019-01-22 16:40:12 -08001680 std::to_string(logId)},
Jason M. Bills424c4172019-03-21 13:50:33 -07001681 {"Name", "CPU Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001682 {"Id", logId},
1683 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001684 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001685 {"Oem", {{"Intel", std::move(j)}}},
1686 {"Created", std::move(t)}};
1687 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001688 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001689 std::move(getStoredLogCallback), CrashdumpObject,
1690 CrashdumpPath + std::string("/") + std::to_string(logId),
1691 "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1692 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001693 }
1694};
1695
Jason M. Bills424c4172019-03-21 13:50:33 -07001696class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001697{
1698 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001699 OnDemandCrashdump(CrowApp &app) :
1700 Node(app,
1701 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1702 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001703 {
1704 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001705 {boost::beast::http::verb::get, {{"Login"}}},
1706 {boost::beast::http::verb::head, {{"Login"}}},
1707 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1708 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1709 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1710 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001711 }
1712
1713 private:
1714 void doPost(crow::Response &res, const crow::Request &req,
1715 const std::vector<std::string> &params) override
1716 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001717 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001718 static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
Ed Tanous1da66f72018-07-27 16:13:37 -07001719
Jason M. Bills48e46392019-02-13 12:58:37 -08001720 // Only allow one OnDemand Log request at a time
1721 if (onDemandLogMatcher != nullptr)
Ed Tanous1da66f72018-07-27 16:13:37 -07001722 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001723 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -07001724 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -07001725 return;
1726 }
1727 // Make this static so it survives outside this method
Ed Tanous271584a2019-07-09 16:24:22 -07001728 static boost::asio::steady_timer timeout(*req.ioService);
Ed Tanous1da66f72018-07-27 16:13:37 -07001729
Ed Tanous271584a2019-07-09 16:24:22 -07001730 timeout.expires_after(std::chrono::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -07001731 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Jason M. Bills48e46392019-02-13 12:58:37 -08001732 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001733 if (ec)
1734 {
1735 // operation_aborted is expected if timer is canceled before
1736 // completion.
1737 if (ec != boost::asio::error::operation_aborted)
1738 {
1739 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
1740 }
1741 return;
1742 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001743 BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
Ed Tanous1da66f72018-07-27 16:13:37 -07001744
Jason M. Billsf12894f2018-10-09 12:45:45 -07001745 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001746 });
1747
Jason M. Bills48e46392019-02-13 12:58:37 -08001748 auto onDemandLogMatcherCallback = [asyncResp](
1749 sdbusplus::message::message &m) {
1750 BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
Ed Tanous271584a2019-07-09 16:24:22 -07001751 timeout.cancel();
1752
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001753 sdbusplus::message::object_path objPath;
Ed Tanous1da66f72018-07-27 16:13:37 -07001754 boost::container::flat_map<
Ed Tanousabf2add2019-01-22 16:40:12 -08001755 std::string, boost::container::flat_map<
1756 std::string, std::variant<std::string>>>
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001757 interfacesAdded;
1758 m.read(objPath, interfacesAdded);
Ed Tanousabf2add2019-01-22 16:40:12 -08001759 const std::string *log = std::get_if<std::string>(
Jason M. Bills424c4172019-03-21 13:50:33 -07001760 &interfacesAdded[CrashdumpInterface]["Log"]);
Ed Tanous1da66f72018-07-27 16:13:37 -07001761 if (log == nullptr)
1762 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001763 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001764 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001765 // match object inside which this lambda is executing. Once it
1766 // is set to nullptr, the match object will be destroyed and the
1767 // lambda will lose its context, including res, so it needs to
1768 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001769 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001770 return;
1771 }
1772 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1773 if (j.is_discarded())
1774 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001775 messages::internalError(asyncResp->res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001776 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001777 // match object inside which this lambda is executing. Once it
1778 // is set to nullptr, the match object will be destroyed and the
1779 // lambda will lose its context, including res, so it needs to
1780 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001781 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001782 return;
1783 }
1784 std::string t = getLogCreatedTime(j);
Jason M. Billse1f26342018-07-18 12:12:00 -07001785 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001786 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001787 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001788 {"Name", "CPU Crashdump"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001789 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001790 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanous1da66f72018-07-27 16:13:37 -07001791 {"Oem", {{"Intel", std::move(j)}}},
1792 {"Created", std::move(t)}};
Jason M. Bills48e46392019-02-13 12:58:37 -08001793 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001794 // match object inside which this lambda is executing. Once it is
1795 // set to nullptr, the match object will be destroyed and the lambda
1796 // will lose its context, including res, so it needs to be the last
1797 // thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001798 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001799 };
Jason M. Bills48e46392019-02-13 12:58:37 -08001800 onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
Ed Tanous1da66f72018-07-27 16:13:37 -07001801 *crow::connections::systemBus,
1802 sdbusplus::bus::match::rules::interfacesAdded() +
Jason M. Bills424c4172019-03-21 13:50:33 -07001803 sdbusplus::bus::match::rules::argNpath(0,
1804 CrashdumpOnDemandPath),
Jason M. Bills48e46392019-02-13 12:58:37 -08001805 std::move(onDemandLogMatcherCallback));
Ed Tanous1da66f72018-07-27 16:13:37 -07001806
Jason M. Bills48e46392019-02-13 12:58:37 -08001807 auto generateonDemandLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -07001808 [asyncResp](const boost::system::error_code ec,
1809 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -07001810 if (ec)
1811 {
1812 if (ec.value() ==
1813 boost::system::errc::operation_not_supported)
1814 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001815 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001816 }
1817 else
1818 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001819 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001820 }
Ed Tanous271584a2019-07-09 16:24:22 -07001821
1822 timeout.cancel();
Jason M. Bills48e46392019-02-13 12:58:37 -08001823 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001824 return;
1825 }
1826 };
1827 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001828 std::move(generateonDemandLogCallback), CrashdumpObject,
1829 CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001830 }
1831};
1832
Jason M. Billse1f26342018-07-18 12:12:00 -07001833class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001834{
1835 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001836 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001837 Node(app,
1838 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1839 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001840 {
1841 entityPrivileges = {
1842 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1843 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1844 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1845 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1846 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1847 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1848 }
1849
1850 private:
1851 void doPost(crow::Response &res, const crow::Request &req,
1852 const std::vector<std::string> &params) override
1853 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001854 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousb1556422018-10-16 14:09:17 -07001855 uint8_t clientAddress = 0;
1856 uint8_t readLength = 0;
Ed Tanous1da66f72018-07-27 16:13:37 -07001857 std::vector<uint8_t> peciCommand;
Ed Tanousb1556422018-10-16 14:09:17 -07001858 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1859 "ReadLength", readLength, "PECICommand",
1860 peciCommand))
Ed Tanous1da66f72018-07-27 16:13:37 -07001861 {
Ed Tanousb1556422018-10-16 14:09:17 -07001862 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001863 }
Ed Tanousb1556422018-10-16 14:09:17 -07001864
Ed Tanous1da66f72018-07-27 16:13:37 -07001865 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001866 auto sendRawPECICallback =
1867 [asyncResp](const boost::system::error_code ec,
1868 const std::vector<uint8_t> &resp) {
1869 if (ec)
1870 {
1871 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
1872 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001873 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001874 return;
1875 }
1876 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1877 {"PECIResponse", resp}};
1878 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001879 // Call the SendRawPECI command with the provided data
1880 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001881 std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1882 CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001883 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -07001884 }
1885};
1886
Andrew Geisslercb92c032018-08-17 07:56:14 -07001887/**
1888 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1889 */
1890class DBusLogServiceActionsClear : public Node
1891{
1892 public:
1893 DBusLogServiceActionsClear(CrowApp &app) :
1894 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
1895 "LogService.Reset")
1896 {
1897 entityPrivileges = {
1898 {boost::beast::http::verb::get, {{"Login"}}},
1899 {boost::beast::http::verb::head, {{"Login"}}},
1900 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1901 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1902 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1903 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1904 }
1905
1906 private:
1907 /**
1908 * Function handles POST method request.
1909 * The Clear Log actions does not require any parameter.The action deletes
1910 * all entries found in the Entries collection for this Log Service.
1911 */
1912 void doPost(crow::Response &res, const crow::Request &req,
1913 const std::vector<std::string> &params) override
1914 {
1915 BMCWEB_LOG_DEBUG << "Do delete all entries.";
1916
1917 auto asyncResp = std::make_shared<AsyncResp>(res);
1918 // Process response from Logging service.
1919 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1920 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1921 if (ec)
1922 {
1923 // TODO Handle for specific error code
1924 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1925 asyncResp->res.result(
1926 boost::beast::http::status::internal_server_error);
1927 return;
1928 }
1929
1930 asyncResp->res.result(boost::beast::http::status::no_content);
1931 };
1932
1933 // Make call to Logging service to request Clear Log
1934 crow::connections::systemBus->async_method_call(
1935 resp_handler, "xyz.openbmc_project.Logging",
1936 "/xyz/openbmc_project/logging",
1937 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1938 }
1939};
Ed Tanous1da66f72018-07-27 16:13:37 -07001940} // namespace redfish