blob: 5248fb37081fcb76f2957964c1730cd208eec43c [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
Tim Lee1f56a3a2019-10-09 10:17:57 +0800511class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700512{
513 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800514 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700515 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
516 "LogService.ClearLog/")
517 {
518 entityPrivileges = {
519 {boost::beast::http::verb::get, {{"Login"}}},
520 {boost::beast::http::verb::head, {{"Login"}}},
521 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
522 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
523 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
524 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
525 }
526
527 private:
528 void doPost(crow::Response &res, const crow::Request &req,
529 const std::vector<std::string> &params) override
530 {
531 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
532
533 // Clear the EventLog by deleting the log files
534 std::vector<std::filesystem::path> redfishLogFiles;
535 if (getRedfishLogFiles(redfishLogFiles))
536 {
537 for (const std::filesystem::path &file : redfishLogFiles)
538 {
539 std::error_code ec;
540 std::filesystem::remove(file, ec);
541 }
542 }
543
544 // Reload rsyslog so it knows to start new log files
545 crow::connections::systemBus->async_method_call(
546 [asyncResp](const boost::system::error_code ec) {
547 if (ec)
548 {
549 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
550 messages::internalError(asyncResp->res);
551 return;
552 }
553
554 messages::success(asyncResp->res);
555 },
556 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
557 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
558 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800559 }
560};
561
Jason M. Bills95820182019-04-22 16:25:34 -0700562static int fillEventLogEntryJson(const std::string &logEntryID,
563 const std::string logEntry,
564 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800565{
Jason M. Bills95820182019-04-22 16:25:34 -0700566 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700567 // First get the Timestamp
568 size_t space = logEntry.find_first_of(" ");
569 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700570 {
571 return 1;
572 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700573 std::string timestamp = logEntry.substr(0, space);
574 // Then get the log contents
575 size_t entryStart = logEntry.find_first_not_of(" ", space);
576 if (entryStart == std::string::npos)
577 {
578 return 1;
579 }
580 std::string_view entry(logEntry);
581 entry.remove_prefix(entryStart);
582 // Use split to separate the entry into its fields
583 std::vector<std::string> logEntryFields;
584 boost::split(logEntryFields, entry, boost::is_any_of(","),
585 boost::token_compress_on);
586 // We need at least a MessageId to be valid
587 if (logEntryFields.size() < 1)
588 {
589 return 1;
590 }
591 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700592
Jason M. Bills4851d452019-03-28 11:27:48 -0700593 // Get the Message from the MessageRegistry
594 const message_registries::Message *message =
595 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800596
Jason M. Bills4851d452019-03-28 11:27:48 -0700597 std::string msg;
598 std::string severity;
599 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800600 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700601 msg = message->message;
602 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800603 }
604
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700605 // Get the MessageArgs from the log if there are any
606 boost::beast::span<std::string> messageArgs;
607 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700608 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700609 std::string &messageArgsStart = logEntryFields[1];
610 // If the first string is empty, assume there are no MessageArgs
611 std::size_t messageArgsSize = 0;
612 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700613 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700614 messageArgsSize = logEntryFields.size() - 1;
615 }
616
617 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
618
619 // Fill the MessageArgs into the Message
620 int i = 0;
621 for (const std::string &messageArg : messageArgs)
622 {
623 std::string argStr = "%" + std::to_string(++i);
624 size_t argPos = msg.find(argStr);
625 if (argPos != std::string::npos)
626 {
627 msg.replace(argPos, argStr.length(), messageArg);
628 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700629 }
630 }
631
Jason M. Bills95820182019-04-22 16:25:34 -0700632 // Get the Created time from the timestamp. The log timestamp is in RFC3339
633 // format which matches the Redfish format except for the fractional seconds
634 // between the '.' and the '+', so just remove them.
635 std::size_t dot = timestamp.find_first_of(".");
636 std::size_t plus = timestamp.find_first_of("+");
637 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800638 {
Jason M. Bills95820182019-04-22 16:25:34 -0700639 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800640 }
641
642 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700643 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700644 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
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();
Ed Tanous66664f22019-10-11 13:05:49 -0700905 uint32_t *id = nullptr;
906 std::time_t timestamp{};
907 std::string *severity = nullptr;
908 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700909 for (auto &propertyMap : interfaceMap.second)
910 {
911 if (propertyMap.first == "Id")
912 {
913 id = sdbusplus::message::variant_ns::get_if<
914 uint32_t>(&propertyMap.second);
915 if (id == nullptr)
916 {
917 messages::propertyMissing(asyncResp->res,
918 "Id");
919 }
920 }
921 else if (propertyMap.first == "Timestamp")
922 {
923 const uint64_t *millisTimeStamp =
924 std::get_if<uint64_t>(&propertyMap.second);
925 if (millisTimeStamp == nullptr)
926 {
927 messages::propertyMissing(asyncResp->res,
928 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -0700929 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700930 }
931 // Retrieve Created property with format:
932 // yyyy-mm-ddThh:mm:ss
933 std::chrono::milliseconds chronoTimeStamp(
934 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -0700935 timestamp = std::chrono::duration_cast<
936 std::chrono::duration<int>>(
937 chronoTimeStamp)
938 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -0700939 }
940 else if (propertyMap.first == "Severity")
941 {
942 severity = std::get_if<std::string>(
943 &propertyMap.second);
944 if (severity == nullptr)
945 {
946 messages::propertyMissing(asyncResp->res,
947 "Severity");
948 }
949 }
950 else if (propertyMap.first == "Message")
951 {
952 message = std::get_if<std::string>(
953 &propertyMap.second);
954 if (message == nullptr)
955 {
956 messages::propertyMissing(asyncResp->res,
957 "Message");
958 }
959 }
960 }
961 thisEntry = {
962 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
963 {"@odata.context", "/redfish/v1/"
964 "$metadata#LogEntry.LogEntry"},
965 {"@odata.id",
966 "/redfish/v1/Systems/system/LogServices/EventLog/"
967 "Entries/" +
968 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -0500969 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700970 {"Id", std::to_string(*id)},
971 {"Message", *message},
972 {"EntryType", "Event"},
973 {"Severity",
974 translateSeverityDbusToRedfish(*severity)},
975 {"Created", crow::utility::getDateTime(timestamp)}};
976 }
977 }
978 std::sort(entriesArray.begin(), entriesArray.end(),
979 [](const nlohmann::json &left,
980 const nlohmann::json &right) {
981 return (left["Id"] <= right["Id"]);
982 });
983 asyncResp->res.jsonValue["Members@odata.count"] =
984 entriesArray.size();
985 },
986 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
987 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800988 }
989};
990
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500991class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800992{
993 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500994 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800995 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -0800996 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
997 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800998 {
999 entityPrivileges = {
1000 {boost::beast::http::verb::get, {{"Login"}}},
1001 {boost::beast::http::verb::head, {{"Login"}}},
1002 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1003 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1004 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1005 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1006 }
1007
1008 private:
1009 void doGet(crow::Response &res, const crow::Request &req,
1010 const std::vector<std::string> &params) override
1011 {
1012 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001013 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001014 {
1015 messages::internalError(asyncResp->res);
1016 return;
1017 }
Ed Tanous029573d2019-02-01 10:57:49 -08001018 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001019
Andrew Geisslercb92c032018-08-17 07:56:14 -07001020 // DBus implementation of EventLog/Entries
1021 // Make call to Logging Service to find all log entry objects
1022 crow::connections::systemBus->async_method_call(
1023 [asyncResp, entryID](const boost::system::error_code ec,
1024 GetManagedPropertyType &resp) {
1025 if (ec)
1026 {
1027 BMCWEB_LOG_ERROR
1028 << "EventLogEntry (DBus) resp_handler got error " << ec;
1029 messages::internalError(asyncResp->res);
1030 return;
1031 }
Ed Tanous66664f22019-10-11 13:05:49 -07001032 uint32_t *id = nullptr;
1033 std::time_t timestamp{};
1034 std::string *severity = nullptr;
1035 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001036 for (auto &propertyMap : resp)
1037 {
1038 if (propertyMap.first == "Id")
1039 {
1040 id = std::get_if<uint32_t>(&propertyMap.second);
1041 if (id == nullptr)
1042 {
1043 messages::propertyMissing(asyncResp->res, "Id");
1044 }
1045 }
1046 else if (propertyMap.first == "Timestamp")
1047 {
1048 const uint64_t *millisTimeStamp =
1049 std::get_if<uint64_t>(&propertyMap.second);
1050 if (millisTimeStamp == nullptr)
1051 {
1052 messages::propertyMissing(asyncResp->res,
1053 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001054 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001055 }
1056 // Retrieve Created property with format:
1057 // yyyy-mm-ddThh:mm:ss
1058 std::chrono::milliseconds chronoTimeStamp(
1059 *millisTimeStamp);
1060 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001061 std::chrono::duration_cast<
1062 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001063 .count();
1064 }
1065 else if (propertyMap.first == "Severity")
1066 {
1067 severity =
1068 std::get_if<std::string>(&propertyMap.second);
1069 if (severity == nullptr)
1070 {
1071 messages::propertyMissing(asyncResp->res,
1072 "Severity");
1073 }
1074 }
1075 else if (propertyMap.first == "Message")
1076 {
1077 message = std::get_if<std::string>(&propertyMap.second);
1078 if (message == nullptr)
1079 {
1080 messages::propertyMissing(asyncResp->res,
1081 "Message");
1082 }
1083 }
1084 }
Ed Tanous271584a2019-07-09 16:24:22 -07001085 if (id == nullptr || message == nullptr || severity == nullptr)
1086 {
1087 return;
1088 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001089 asyncResp->res.jsonValue = {
1090 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1091 {"@odata.context", "/redfish/v1/"
1092 "$metadata#LogEntry.LogEntry"},
1093 {"@odata.id",
1094 "/redfish/v1/Systems/system/LogServices/EventLog/"
1095 "Entries/" +
1096 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001097 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001098 {"Id", std::to_string(*id)},
1099 {"Message", *message},
1100 {"EntryType", "Event"},
1101 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001102 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001103 },
1104 "xyz.openbmc_project.Logging",
1105 "/xyz/openbmc_project/logging/entry/" + entryID,
1106 "org.freedesktop.DBus.Properties", "GetAll",
1107 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001108 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001109
1110 void doDelete(crow::Response &res, const crow::Request &req,
1111 const std::vector<std::string> &params) override
1112 {
1113
1114 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1115
1116 auto asyncResp = std::make_shared<AsyncResp>(res);
1117
1118 if (params.size() != 1)
1119 {
1120 messages::internalError(asyncResp->res);
1121 return;
1122 }
1123 std::string entryID = params[0];
1124
1125 dbus::utility::escapePathForDbus(entryID);
1126
1127 // Process response from Logging service.
1128 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1129 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1130 if (ec)
1131 {
1132 // TODO Handle for specific error code
1133 BMCWEB_LOG_ERROR
1134 << "EventLogEntry (DBus) doDelete respHandler got error "
1135 << ec;
1136 asyncResp->res.result(
1137 boost::beast::http::status::internal_server_error);
1138 return;
1139 }
1140
1141 asyncResp->res.result(boost::beast::http::status::ok);
1142 };
1143
1144 // Make call to Logging service to request Delete Log
1145 crow::connections::systemBus->async_method_call(
1146 respHandler, "xyz.openbmc_project.Logging",
1147 "/xyz/openbmc_project/logging/entry/" + entryID,
1148 "xyz.openbmc_project.Object.Delete", "Delete");
1149 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001150};
1151
1152class BMCLogServiceCollection : public Node
1153{
1154 public:
1155 template <typename CrowApp>
1156 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001157 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001158 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001159 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001160 {boost::beast::http::verb::get, {{"Login"}}},
1161 {boost::beast::http::verb::head, {{"Login"}}},
1162 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1163 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1164 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1165 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001166 }
1167
1168 private:
1169 /**
1170 * Functions triggers appropriate requests on DBus
1171 */
1172 void doGet(crow::Response &res, const crow::Request &req,
1173 const std::vector<std::string> &params) override
1174 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001175 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001176 // Collections don't include the static data added by SubRoute because
1177 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001178 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001179 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001180 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001181 "/redfish/v1/$metadata#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001182 asyncResp->res.jsonValue["@odata.id"] =
1183 "/redfish/v1/Managers/bmc/LogServices";
1184 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1185 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001186 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001187 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1188 logServiceArray = nlohmann::json::array();
1189#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1190 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001191 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001192#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001193 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001194 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001195 }
1196};
1197
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001198class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001199{
1200 public:
1201 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001202 BMCJournalLogService(CrowApp &app) :
1203 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001204 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001205 entityPrivileges = {
1206 {boost::beast::http::verb::get, {{"Login"}}},
1207 {boost::beast::http::verb::head, {{"Login"}}},
1208 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1209 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1210 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1211 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1212 }
1213
1214 private:
1215 void doGet(crow::Response &res, const crow::Request &req,
1216 const std::vector<std::string> &params) override
1217 {
1218 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001219 asyncResp->res.jsonValue["@odata.type"] =
1220 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001221 asyncResp->res.jsonValue["@odata.id"] =
1222 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001223 asyncResp->res.jsonValue["@odata.context"] =
1224 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001225 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1226 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1227 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001228 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001229 asyncResp->res.jsonValue["Entries"] = {
1230 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001231 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001232 }
1233};
1234
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001235static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1236 sd_journal *journal,
1237 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001238{
1239 // Get the Log Entry contents
1240 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001241
Ed Tanous39e77502019-03-04 17:35:53 -08001242 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001243 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001244 if (ret < 0)
1245 {
1246 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1247 return 1;
1248 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001249
1250 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001251 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001252 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001253 if (ret < 0)
1254 {
1255 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001256 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001257
1258 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001259 std::string entryTimeStr;
1260 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001261 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001262 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001263 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001264
1265 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001266 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001267 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001268 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001269 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1270 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001271 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001272 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001273 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001274 {"EntryType", "Oem"},
1275 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001276 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001277 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001278 {"Created", std::move(entryTimeStr)}};
1279 return 0;
1280}
1281
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001282class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001283{
1284 public:
1285 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001286 BMCJournalLogEntryCollection(CrowApp &app) :
1287 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001288 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001289 entityPrivileges = {
1290 {boost::beast::http::verb::get, {{"Login"}}},
1291 {boost::beast::http::verb::head, {{"Login"}}},
1292 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1293 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1294 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1295 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1296 }
1297
1298 private:
1299 void doGet(crow::Response &res, const crow::Request &req,
1300 const std::vector<std::string> &params) override
1301 {
1302 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001303 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001304 uint64_t skip = 0;
1305 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001306 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001307 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001308 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001309 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001310 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001311 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001312 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001313 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001314 // Collections don't include the static data added by SubRoute because
1315 // it has a duplicate entry for members
1316 asyncResp->res.jsonValue["@odata.type"] =
1317 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001318 asyncResp->res.jsonValue["@odata.id"] =
1319 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001320 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001321 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001322 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001323 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001324 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1325 asyncResp->res.jsonValue["Description"] =
1326 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001327 asyncResp->res.jsonValue["@odata.id"] =
1328 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001329 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1330 logEntryArray = nlohmann::json::array();
1331
1332 // Go through the journal and use the timestamp to create a unique ID
1333 // for each entry
1334 sd_journal *journalTmp = nullptr;
1335 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1336 if (ret < 0)
1337 {
1338 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001339 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001340 return;
1341 }
1342 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1343 journalTmp, sd_journal_close);
1344 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001345 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001346 // Reset the unique ID on the first entry
1347 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001348 SD_JOURNAL_FOREACH(journal.get())
1349 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001350 entryCount++;
1351 // Handle paging using skip (number of entries to skip from the
1352 // start) and top (number of entries to display)
1353 if (entryCount <= skip || entryCount > skip + top)
1354 {
1355 continue;
1356 }
1357
Jason M. Bills16428a12018-11-02 12:42:29 -07001358 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001359 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001360 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001361 continue;
1362 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001363
Jason M. Billse85d6b12019-07-29 17:01:15 -07001364 if (firstEntry)
1365 {
1366 firstEntry = false;
1367 }
1368
Jason M. Billse1f26342018-07-18 12:12:00 -07001369 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001370 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1371 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1372 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001373 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001374 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001375 return;
1376 }
1377 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001378 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1379 if (skip + top < entryCount)
1380 {
1381 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001382 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001383 std::to_string(skip + top);
1384 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001385 }
1386};
1387
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001388class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001389{
1390 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001391 BMCJournalLogEntry(CrowApp &app) :
1392 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001393 std::string())
1394 {
1395 entityPrivileges = {
1396 {boost::beast::http::verb::get, {{"Login"}}},
1397 {boost::beast::http::verb::head, {{"Login"}}},
1398 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1399 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1400 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1401 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1402 }
1403
1404 private:
1405 void doGet(crow::Response &res, const crow::Request &req,
1406 const std::vector<std::string> &params) override
1407 {
1408 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1409 if (params.size() != 1)
1410 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001411 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001412 return;
1413 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001414 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001415 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001416 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001417 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001418 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001419 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001420 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001421 }
1422
1423 sd_journal *journalTmp = nullptr;
1424 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1425 if (ret < 0)
1426 {
1427 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001428 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001429 return;
1430 }
1431 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1432 journalTmp, sd_journal_close);
1433 journalTmp = nullptr;
1434 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001435 // tracking the unique ID
1436 std::string idStr;
1437 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001438 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001439 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001440 {
1441 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001442 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1443 {
1444 messages::internalError(asyncResp->res);
1445 return;
1446 }
1447 if (firstEntry)
1448 {
1449 firstEntry = false;
1450 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001451 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001452 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001453 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001454 {
1455 messages::resourceMissingAtURI(asyncResp->res, entryID);
1456 return;
1457 }
1458
1459 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1460 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001461 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001462 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001463 return;
1464 }
1465 }
1466};
1467
Jason M. Bills424c4172019-03-21 13:50:33 -07001468class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001469{
1470 public:
1471 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001472 CrashdumpService(CrowApp &app) :
1473 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001474 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001475 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001476 {boost::beast::http::verb::get, {{"Login"}}},
1477 {boost::beast::http::verb::head, {{"Login"}}},
1478 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1479 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1480 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1481 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001482 }
1483
1484 private:
1485 /**
1486 * Functions triggers appropriate requests on DBus
1487 */
1488 void doGet(crow::Response &res, const crow::Request &req,
1489 const std::vector<std::string> &params) override
1490 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001491 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001492 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001493 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001494 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001495 asyncResp->res.jsonValue["@odata.type"] =
1496 "#LogService.v1_1_0.LogService";
1497 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001498 "/redfish/v1/$metadata#LogService.LogService";
Jason M. Bills424c4172019-03-21 13:50:33 -07001499 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Service";
1500 asyncResp->res.jsonValue["Description"] = "Crashdump Service";
1501 asyncResp->res.jsonValue["Id"] = "Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001502 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1503 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001504 asyncResp->res.jsonValue["Entries"] = {
1505 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001506 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001507 asyncResp->res.jsonValue["Actions"] = {
Ed Tanous1da66f72018-07-27 16:13:37 -07001508 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001509 {{"#Crashdump.OnDemand",
1510 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1511 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001512
1513#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001514 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001515 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001516 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1517 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001518#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001519 }
1520};
1521
Jason M. Billse855dd22019-10-08 11:37:48 -07001522std::string getLogCreatedTime(const std::string &crashdump)
1523{
1524 nlohmann::json crashdumpJson =
1525 nlohmann::json::parse(crashdump, nullptr, false);
1526 if (crashdumpJson.is_discarded())
1527 {
1528 return std::string();
1529 }
1530
1531 nlohmann::json::const_iterator cdIt = crashdumpJson.find("crash_data");
1532 if (cdIt == crashdumpJson.end())
1533 {
1534 return std::string();
1535 }
1536
1537 nlohmann::json::const_iterator siIt = cdIt->find("METADATA");
1538 if (siIt == cdIt->end())
1539 {
1540 return std::string();
1541 }
1542
1543 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1544 if (tsIt == siIt->end())
1545 {
1546 return std::string();
1547 }
1548
1549 const std::string *logTime = tsIt->get_ptr<const std::string *>();
1550 if (logTime == nullptr)
1551 {
1552 return std::string();
1553 }
1554
1555 std::string redfishDateTime = *logTime;
1556 if (redfishDateTime.length() > 2)
1557 {
1558 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1559 }
1560
1561 return redfishDateTime;
1562}
1563
1564std::string getLogFileName(const std::string &logTime)
1565{
1566 // Set the crashdump file name to "crashdump_<logTime>.json" using the
1567 // created time without the timezone info
1568 std::string fileTime = logTime;
1569 size_t plusPos = fileTime.rfind('+');
1570 if (plusPos != std::string::npos)
1571 {
1572 fileTime.erase(plusPos);
1573 }
1574 return "crashdump_" + fileTime + ".json";
1575}
1576
1577static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1578 const std::string &logID,
1579 nlohmann::json &logEntryJson)
1580{
1581 auto getStoredLogCallback = [asyncResp, logID, &logEntryJson](
1582 const boost::system::error_code ec,
1583 const std::variant<std::string> &resp) {
1584 if (ec)
1585 {
1586 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1587 messages::internalError(asyncResp->res);
1588 return;
1589 }
1590 const std::string *log = std::get_if<std::string>(&resp);
1591 if (log == nullptr)
1592 {
1593 messages::internalError(asyncResp->res);
1594 return;
1595 }
1596 std::string logTime = getLogCreatedTime(*log);
1597 std::string fileName = getLogFileName(logTime);
1598
1599 logEntryJson = {
1600 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
1601 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1602 {"@odata.id",
1603 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1604 logID},
1605 {"Name", "CPU Crashdump"},
1606 {"Id", logID},
1607 {"EntryType", "Oem"},
1608 {"OemRecordFormat", "Crashdump URI"},
1609 {"Message",
1610 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1611 logID + "/" + fileName},
1612 {"Created", std::move(logTime)}};
1613 };
1614 crow::connections::systemBus->async_method_call(
1615 std::move(getStoredLogCallback), CrashdumpObject,
1616 CrashdumpPath + std::string("/") + logID,
1617 "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface, "Log");
1618}
1619
Jason M. Bills424c4172019-03-21 13:50:33 -07001620class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001621{
1622 public:
1623 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001624 CrashdumpEntryCollection(CrowApp &app) :
1625 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001626 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001627 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001628 {boost::beast::http::verb::get, {{"Login"}}},
1629 {boost::beast::http::verb::head, {{"Login"}}},
1630 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1631 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1632 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1633 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001634 }
1635
1636 private:
1637 /**
1638 * Functions triggers appropriate requests on DBus
1639 */
1640 void doGet(crow::Response &res, const crow::Request &req,
1641 const std::vector<std::string> &params) override
1642 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001643 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001644 // Collections don't include the static data added by SubRoute because
1645 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001646 auto getLogEntriesCallback = [asyncResp](
1647 const boost::system::error_code ec,
1648 const std::vector<std::string> &resp) {
1649 if (ec)
1650 {
1651 if (ec.value() !=
1652 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001653 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001654 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1655 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001656 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001657 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001658 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001659 }
1660 asyncResp->res.jsonValue["@odata.type"] =
1661 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001662 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001663 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001664 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billse855dd22019-10-08 11:37:48 -07001665 "/redfish/v1/$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Bills424c4172019-03-21 13:50:33 -07001666 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001667 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001668 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001669 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1670 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07001671 std::vector<std::string> logIDs;
1672 // Get the list of log entries and build up an empty array big
1673 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07001674 for (const std::string &objpath : resp)
1675 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001676 // Ignore the on-demand log
Jason M. Bills424c4172019-03-21 13:50:33 -07001677 if (objpath.compare(CrashdumpOnDemandPath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -07001678 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001679 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -07001680 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001681
1682 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07001683 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07001684 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07001685 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001686 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07001687 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001688 logIDs.emplace_back(objpath.substr(lastPos + 1));
1689
1690 // Add a space for the log entry to the array
1691 logEntryArray.push_back({});
1692 }
1693 // Now go through and set up async calls to fill in the entries
1694 size_t index = 0;
1695 for (const std::string &logID : logIDs)
1696 {
1697 // Add the log entry to the array
1698 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07001699 }
1700 asyncResp->res.jsonValue["Members@odata.count"] =
1701 logEntryArray.size();
1702 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001703 crow::connections::systemBus->async_method_call(
1704 std::move(getLogEntriesCallback),
1705 "xyz.openbmc_project.ObjectMapper",
1706 "/xyz/openbmc_project/object_mapper",
1707 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills424c4172019-03-21 13:50:33 -07001708 std::array<const char *, 1>{CrashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001709 }
1710};
1711
Jason M. Bills424c4172019-03-21 13:50:33 -07001712class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001713{
1714 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001715 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001716 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001717 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001718 std::string())
1719 {
1720 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001721 {boost::beast::http::verb::get, {{"Login"}}},
1722 {boost::beast::http::verb::head, {{"Login"}}},
1723 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1724 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1725 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1726 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001727 }
1728
1729 private:
1730 void doGet(crow::Response &res, const crow::Request &req,
1731 const std::vector<std::string> &params) override
1732 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001733 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001734 if (params.size() != 1)
1735 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001736 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001737 return;
1738 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001739 const std::string &logID = params[0];
1740 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
1741 }
1742};
1743
1744class CrashdumpFile : public Node
1745{
1746 public:
1747 CrashdumpFile(CrowApp &app) :
1748 Node(app,
1749 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
1750 "<str>/",
1751 std::string(), std::string())
1752 {
1753 entityPrivileges = {
1754 {boost::beast::http::verb::get, {{"Login"}}},
1755 {boost::beast::http::verb::head, {{"Login"}}},
1756 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1757 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1758 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1759 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1760 }
1761
1762 private:
1763 void doGet(crow::Response &res, const crow::Request &req,
1764 const std::vector<std::string> &params) override
1765 {
1766 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1767 if (params.size() != 2)
1768 {
1769 messages::internalError(asyncResp->res);
1770 return;
1771 }
1772 const std::string &logID = params[0];
1773 const std::string &fileName = params[1];
1774
1775 auto getStoredLogCallback = [asyncResp, logID, fileName](
Ed Tanousabf2add2019-01-22 16:40:12 -08001776 const boost::system::error_code ec,
1777 const std::variant<std::string> &resp) {
1778 if (ec)
1779 {
1780 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1781 messages::internalError(asyncResp->res);
1782 return;
1783 }
1784 const std::string *log = std::get_if<std::string>(&resp);
1785 if (log == nullptr)
1786 {
1787 messages::internalError(asyncResp->res);
1788 return;
1789 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001790
1791 // Verify the file name parameter is correct
1792 if (fileName != getLogFileName(getLogCreatedTime(*log)))
Ed Tanousabf2add2019-01-22 16:40:12 -08001793 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001794 messages::resourceMissingAtURI(asyncResp->res, fileName);
Ed Tanousabf2add2019-01-22 16:40:12 -08001795 return;
1796 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001797
1798 // Configure this to be a file download when accessed from a browser
1799 asyncResp->res.addHeader("Content-Disposition", "attachment");
1800 asyncResp->res.body() = *log;
Ed Tanousabf2add2019-01-22 16:40:12 -08001801 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001802 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001803 std::move(getStoredLogCallback), CrashdumpObject,
Jason M. Billse855dd22019-10-08 11:37:48 -07001804 CrashdumpPath + std::string("/") + logID,
Jason M. Bills424c4172019-03-21 13:50:33 -07001805 "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1806 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001807 }
1808};
1809
Jason M. Bills424c4172019-03-21 13:50:33 -07001810class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001811{
1812 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001813 OnDemandCrashdump(CrowApp &app) :
1814 Node(app,
1815 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1816 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001817 {
1818 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001819 {boost::beast::http::verb::get, {{"Login"}}},
1820 {boost::beast::http::verb::head, {{"Login"}}},
1821 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1822 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1823 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1824 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001825 }
1826
1827 private:
1828 void doPost(crow::Response &res, const crow::Request &req,
1829 const std::vector<std::string> &params) override
1830 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001831 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001832 static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
Ed Tanous1da66f72018-07-27 16:13:37 -07001833
Jason M. Bills48e46392019-02-13 12:58:37 -08001834 // Only allow one OnDemand Log request at a time
1835 if (onDemandLogMatcher != nullptr)
Ed Tanous1da66f72018-07-27 16:13:37 -07001836 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001837 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -07001838 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -07001839 return;
1840 }
1841 // Make this static so it survives outside this method
Ed Tanous271584a2019-07-09 16:24:22 -07001842 static boost::asio::steady_timer timeout(*req.ioService);
Ed Tanous1da66f72018-07-27 16:13:37 -07001843
Ed Tanous271584a2019-07-09 16:24:22 -07001844 timeout.expires_after(std::chrono::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -07001845 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Jason M. Bills48e46392019-02-13 12:58:37 -08001846 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001847 if (ec)
1848 {
1849 // operation_aborted is expected if timer is canceled before
1850 // completion.
1851 if (ec != boost::asio::error::operation_aborted)
1852 {
1853 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
1854 }
1855 return;
1856 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001857 BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
Ed Tanous1da66f72018-07-27 16:13:37 -07001858
Jason M. Billsf12894f2018-10-09 12:45:45 -07001859 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001860 });
1861
Jason M. Bills3cf8ea32019-10-08 11:43:32 -07001862 auto onDemandLogMatcherCallback =
1863 [asyncResp](sdbusplus::message::message &m) {
1864 BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
1865 timeout.cancel();
Ed Tanous271584a2019-07-09 16:24:22 -07001866
Jason M. Bills3cf8ea32019-10-08 11:43:32 -07001867 sdbusplus::message::object_path objPath;
1868 boost::container::flat_map<
1869 std::string, boost::container::flat_map<
1870 std::string, std::variant<std::string>>>
1871 interfacesAdded;
1872 m.read(objPath, interfacesAdded);
1873 const std::string *log = std::get_if<std::string>(
1874 &interfacesAdded[CrashdumpInterface]["Log"]);
1875 if (log == nullptr)
1876 {
1877 messages::internalError(asyncResp->res);
1878 // Careful with onDemandLogMatcher. It is a unique_ptr to
1879 // the match object inside which this lambda is executing.
1880 // Once it is set to nullptr, the match object will be
1881 // destroyed and the lambda will lose its context, including
1882 // res, so it needs to be the last thing done.
1883 onDemandLogMatcher = nullptr;
1884 return;
1885 }
1886 nlohmann::json crashdumpJson =
1887 nlohmann::json::parse(*log, nullptr, false);
1888 if (crashdumpJson.is_discarded())
1889 {
1890 messages::internalError(asyncResp->res);
1891 // Careful with onDemandLogMatcher. It is a unique_ptr to
1892 // the match object inside which this lambda is executing.
1893 // Once it is set to nullptr, the match object will be
1894 // destroyed and the lambda will lose its context, including
1895 // res, so it needs to be the last thing done.
1896 onDemandLogMatcher = nullptr;
1897 return;
1898 }
1899 asyncResp->res.jsonValue = crashdumpJson;
Jason M. Bills48e46392019-02-13 12:58:37 -08001900 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001901 // match object inside which this lambda is executing. Once it
1902 // is set to nullptr, the match object will be destroyed and the
1903 // lambda will lose its context, including res, so it needs to
1904 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001905 onDemandLogMatcher = nullptr;
Jason M. Bills3cf8ea32019-10-08 11:43:32 -07001906 };
Jason M. Bills48e46392019-02-13 12:58:37 -08001907 onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
Ed Tanous1da66f72018-07-27 16:13:37 -07001908 *crow::connections::systemBus,
1909 sdbusplus::bus::match::rules::interfacesAdded() +
Jason M. Bills424c4172019-03-21 13:50:33 -07001910 sdbusplus::bus::match::rules::argNpath(0,
1911 CrashdumpOnDemandPath),
Jason M. Bills48e46392019-02-13 12:58:37 -08001912 std::move(onDemandLogMatcherCallback));
Ed Tanous1da66f72018-07-27 16:13:37 -07001913
Jason M. Bills48e46392019-02-13 12:58:37 -08001914 auto generateonDemandLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -07001915 [asyncResp](const boost::system::error_code ec,
1916 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -07001917 if (ec)
1918 {
1919 if (ec.value() ==
1920 boost::system::errc::operation_not_supported)
1921 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001922 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001923 }
1924 else
1925 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001926 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001927 }
Ed Tanous271584a2019-07-09 16:24:22 -07001928
1929 timeout.cancel();
Jason M. Bills48e46392019-02-13 12:58:37 -08001930 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001931 return;
1932 }
1933 };
1934 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001935 std::move(generateonDemandLogCallback), CrashdumpObject,
1936 CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001937 }
1938};
1939
Jason M. Billse1f26342018-07-18 12:12:00 -07001940class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001941{
1942 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001943 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001944 Node(app,
1945 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1946 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001947 {
1948 entityPrivileges = {
1949 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1950 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1951 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1952 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1953 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1954 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1955 }
1956
1957 private:
1958 void doPost(crow::Response &res, const crow::Request &req,
1959 const std::vector<std::string> &params) override
1960 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001961 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousb1556422018-10-16 14:09:17 -07001962 uint8_t clientAddress = 0;
1963 uint8_t readLength = 0;
Ed Tanous1da66f72018-07-27 16:13:37 -07001964 std::vector<uint8_t> peciCommand;
Ed Tanousb1556422018-10-16 14:09:17 -07001965 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1966 "ReadLength", readLength, "PECICommand",
1967 peciCommand))
Ed Tanous1da66f72018-07-27 16:13:37 -07001968 {
Ed Tanousb1556422018-10-16 14:09:17 -07001969 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001970 }
Ed Tanousb1556422018-10-16 14:09:17 -07001971
Ed Tanous1da66f72018-07-27 16:13:37 -07001972 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001973 auto sendRawPECICallback =
1974 [asyncResp](const boost::system::error_code ec,
1975 const std::vector<uint8_t> &resp) {
1976 if (ec)
1977 {
1978 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
1979 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001980 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001981 return;
1982 }
1983 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1984 {"PECIResponse", resp}};
1985 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001986 // Call the SendRawPECI command with the provided data
1987 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001988 std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1989 CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001990 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -07001991 }
1992};
1993
Andrew Geisslercb92c032018-08-17 07:56:14 -07001994/**
1995 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1996 */
1997class DBusLogServiceActionsClear : public Node
1998{
1999 public:
2000 DBusLogServiceActionsClear(CrowApp &app) :
2001 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Tim Lee1f56a3a2019-10-09 10:17:57 +08002002 "LogService.ClearLog")
Andrew Geisslercb92c032018-08-17 07:56:14 -07002003 {
2004 entityPrivileges = {
2005 {boost::beast::http::verb::get, {{"Login"}}},
2006 {boost::beast::http::verb::head, {{"Login"}}},
2007 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
2008 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
2009 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
2010 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
2011 }
2012
2013 private:
2014 /**
2015 * Function handles POST method request.
2016 * The Clear Log actions does not require any parameter.The action deletes
2017 * all entries found in the Entries collection for this Log Service.
2018 */
2019 void doPost(crow::Response &res, const crow::Request &req,
2020 const std::vector<std::string> &params) override
2021 {
2022 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2023
2024 auto asyncResp = std::make_shared<AsyncResp>(res);
2025 // Process response from Logging service.
2026 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2027 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2028 if (ec)
2029 {
2030 // TODO Handle for specific error code
2031 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2032 asyncResp->res.result(
2033 boost::beast::http::status::internal_server_error);
2034 return;
2035 }
2036
2037 asyncResp->res.result(boost::beast::http::status::no_content);
2038 };
2039
2040 // Make call to Logging service to request Clear Log
2041 crow::connections::systemBus->async_method_call(
2042 resp_handler, "xyz.openbmc_project.Logging",
2043 "/xyz/openbmc_project/logging",
2044 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2045 }
2046};
Ed Tanous1da66f72018-07-27 16:13:37 -07002047} // namespace redfish