blob: f1bfda6406ef87dc0b6f95be1006d500bbfab255 [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. Bills424c4172019-03-21 13:50:33 -07001522class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001523{
1524 public:
1525 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001526 CrashdumpEntryCollection(CrowApp &app) :
1527 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001528 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001529 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001530 {boost::beast::http::verb::get, {{"Login"}}},
1531 {boost::beast::http::verb::head, {{"Login"}}},
1532 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1533 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1534 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1535 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001536 }
1537
1538 private:
1539 /**
1540 * Functions triggers appropriate requests on DBus
1541 */
1542 void doGet(crow::Response &res, const crow::Request &req,
1543 const std::vector<std::string> &params) override
1544 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001545 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001546 // Collections don't include the static data added by SubRoute because
1547 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001548 auto getLogEntriesCallback = [asyncResp](
1549 const boost::system::error_code ec,
1550 const std::vector<std::string> &resp) {
1551 if (ec)
1552 {
1553 if (ec.value() !=
1554 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001555 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001556 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1557 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001558 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001559 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001560 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001561 }
1562 asyncResp->res.jsonValue["@odata.type"] =
1563 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001564 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001565 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001566 asyncResp->res.jsonValue["@odata.context"] =
Jason M. Billsd53dd412019-02-12 17:16:22 -08001567 "/redfish/v1/"
1568 "$metadata#LogEntryCollection.LogEntryCollection";
Jason M. Bills424c4172019-03-21 13:50:33 -07001569 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001570 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001571 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001572 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1573 logEntryArray = nlohmann::json::array();
1574 for (const std::string &objpath : resp)
1575 {
Jason M. Bills48e46392019-02-13 12:58:37 -08001576 // Don't list the on-demand log
Jason M. Bills424c4172019-03-21 13:50:33 -07001577 if (objpath.compare(CrashdumpOnDemandPath) == 0)
Ed Tanous1da66f72018-07-27 16:13:37 -07001578 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001579 continue;
Ed Tanous1da66f72018-07-27 16:13:37 -07001580 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001581 std::size_t lastPos = objpath.rfind("/");
1582 if (lastPos != std::string::npos)
1583 {
1584 logEntryArray.push_back(
Jason M. Billsd53dd412019-02-12 17:16:22 -08001585 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/"
Jason M. Bills424c4172019-03-21 13:50:33 -07001586 "Crashdump/Entries/" +
Jason M. Billse1f26342018-07-18 12:12:00 -07001587 objpath.substr(lastPos + 1)}});
1588 }
1589 }
1590 asyncResp->res.jsonValue["Members@odata.count"] =
1591 logEntryArray.size();
1592 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001593 crow::connections::systemBus->async_method_call(
1594 std::move(getLogEntriesCallback),
1595 "xyz.openbmc_project.ObjectMapper",
1596 "/xyz/openbmc_project/object_mapper",
1597 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills424c4172019-03-21 13:50:33 -07001598 std::array<const char *, 1>{CrashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001599 }
1600};
1601
Jason M. Bills424c4172019-03-21 13:50:33 -07001602std::string getLogCreatedTime(const nlohmann::json &Crashdump)
Ed Tanous1da66f72018-07-27 16:13:37 -07001603{
Jason M. Bills424c4172019-03-21 13:50:33 -07001604 nlohmann::json::const_iterator cdIt = Crashdump.find("crashlog_data");
1605 if (cdIt != Crashdump.end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001606 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001607 nlohmann::json::const_iterator siIt = cdIt->find("SYSTEM_INFO");
1608 if (siIt != cdIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001609 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001610 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1611 if (tsIt != siIt->end())
Ed Tanous1da66f72018-07-27 16:13:37 -07001612 {
Jason M. Billsc4d00432019-02-12 17:17:48 -08001613 const std::string *logTime =
1614 tsIt->get_ptr<const std::string *>();
1615 if (logTime != nullptr)
1616 {
1617 return *logTime;
1618 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001619 }
1620 }
1621 }
1622 BMCWEB_LOG_DEBUG << "failed to find log timestamp";
1623
1624 return std::string();
1625}
1626
Jason M. Bills424c4172019-03-21 13:50:33 -07001627class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001628{
1629 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001630 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001631 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001632 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001633 std::string())
1634 {
1635 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001636 {boost::beast::http::verb::get, {{"Login"}}},
1637 {boost::beast::http::verb::head, {{"Login"}}},
1638 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1639 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1640 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1641 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001642 }
1643
1644 private:
1645 void doGet(crow::Response &res, const crow::Request &req,
1646 const std::vector<std::string> &params) override
1647 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001648 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001649 if (params.size() != 1)
1650 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001651 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001652 return;
1653 }
Ed Tanous271584a2019-07-09 16:24:22 -07001654 const int logId = std::atoi(params[0].c_str());
Ed Tanousabf2add2019-01-22 16:40:12 -08001655 auto getStoredLogCallback = [asyncResp, logId](
1656 const boost::system::error_code ec,
1657 const std::variant<std::string> &resp) {
1658 if (ec)
1659 {
1660 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1661 messages::internalError(asyncResp->res);
1662 return;
1663 }
1664 const std::string *log = std::get_if<std::string>(&resp);
1665 if (log == nullptr)
1666 {
1667 messages::internalError(asyncResp->res);
1668 return;
1669 }
1670 nlohmann::json j = nlohmann::json::parse(*log, nullptr, false);
1671 if (j.is_discarded())
1672 {
1673 messages::internalError(asyncResp->res);
1674 return;
1675 }
1676 std::string t = getLogCreatedTime(j);
1677 asyncResp->res.jsonValue = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001678 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001679 {"@odata.context", "/redfish/v1/$metadata#LogEntry.LogEntry"},
1680 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001681 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
Ed Tanousabf2add2019-01-22 16:40:12 -08001682 std::to_string(logId)},
Jason M. Bills424c4172019-03-21 13:50:33 -07001683 {"Name", "CPU Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001684 {"Id", logId},
1685 {"EntryType", "Oem"},
Jason M. Bills424c4172019-03-21 13:50:33 -07001686 {"OemRecordFormat", "Intel Crashdump"},
Ed Tanousabf2add2019-01-22 16:40:12 -08001687 {"Oem", {{"Intel", std::move(j)}}},
1688 {"Created", std::move(t)}};
1689 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001690 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001691 std::move(getStoredLogCallback), CrashdumpObject,
1692 CrashdumpPath + std::string("/") + std::to_string(logId),
1693 "org.freedesktop.DBus.Properties", "Get", CrashdumpInterface,
1694 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001695 }
1696};
1697
Jason M. Bills424c4172019-03-21 13:50:33 -07001698class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001699{
1700 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001701 OnDemandCrashdump(CrowApp &app) :
1702 Node(app,
1703 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1704 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001705 {
1706 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001707 {boost::beast::http::verb::get, {{"Login"}}},
1708 {boost::beast::http::verb::head, {{"Login"}}},
1709 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1710 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1711 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1712 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001713 }
1714
1715 private:
1716 void doPost(crow::Response &res, const crow::Request &req,
1717 const std::vector<std::string> &params) override
1718 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001719 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills48e46392019-02-13 12:58:37 -08001720 static std::unique_ptr<sdbusplus::bus::match::match> onDemandLogMatcher;
Ed Tanous1da66f72018-07-27 16:13:37 -07001721
Jason M. Bills48e46392019-02-13 12:58:37 -08001722 // Only allow one OnDemand Log request at a time
1723 if (onDemandLogMatcher != nullptr)
Ed Tanous1da66f72018-07-27 16:13:37 -07001724 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001725 asyncResp->res.addHeader("Retry-After", "30");
Jason M. Billsf12894f2018-10-09 12:45:45 -07001726 messages::serviceTemporarilyUnavailable(asyncResp->res, "30");
Ed Tanous1da66f72018-07-27 16:13:37 -07001727 return;
1728 }
1729 // Make this static so it survives outside this method
Ed Tanous271584a2019-07-09 16:24:22 -07001730 static boost::asio::steady_timer timeout(*req.ioService);
Ed Tanous1da66f72018-07-27 16:13:37 -07001731
Ed Tanous271584a2019-07-09 16:24:22 -07001732 timeout.expires_after(std::chrono::seconds(30));
Jason M. Billse1f26342018-07-18 12:12:00 -07001733 timeout.async_wait([asyncResp](const boost::system::error_code &ec) {
Jason M. Bills48e46392019-02-13 12:58:37 -08001734 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001735 if (ec)
1736 {
1737 // operation_aborted is expected if timer is canceled before
1738 // completion.
1739 if (ec != boost::asio::error::operation_aborted)
1740 {
1741 BMCWEB_LOG_ERROR << "Async_wait failed " << ec;
1742 }
1743 return;
1744 }
Jason M. Bills48e46392019-02-13 12:58:37 -08001745 BMCWEB_LOG_ERROR << "Timed out waiting for on-demand log";
Ed Tanous1da66f72018-07-27 16:13:37 -07001746
Jason M. Billsf12894f2018-10-09 12:45:45 -07001747 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001748 });
1749
Jason M. Bills3cf8ea32019-10-08 11:43:32 -07001750 auto onDemandLogMatcherCallback =
1751 [asyncResp](sdbusplus::message::message &m) {
1752 BMCWEB_LOG_DEBUG << "OnDemand log available match fired";
1753 timeout.cancel();
Ed Tanous271584a2019-07-09 16:24:22 -07001754
Jason M. Bills3cf8ea32019-10-08 11:43:32 -07001755 sdbusplus::message::object_path objPath;
1756 boost::container::flat_map<
1757 std::string, boost::container::flat_map<
1758 std::string, std::variant<std::string>>>
1759 interfacesAdded;
1760 m.read(objPath, interfacesAdded);
1761 const std::string *log = std::get_if<std::string>(
1762 &interfacesAdded[CrashdumpInterface]["Log"]);
1763 if (log == nullptr)
1764 {
1765 messages::internalError(asyncResp->res);
1766 // Careful with onDemandLogMatcher. It is a unique_ptr to
1767 // the match object inside which this lambda is executing.
1768 // Once it is set to nullptr, the match object will be
1769 // destroyed and the lambda will lose its context, including
1770 // res, so it needs to be the last thing done.
1771 onDemandLogMatcher = nullptr;
1772 return;
1773 }
1774 nlohmann::json crashdumpJson =
1775 nlohmann::json::parse(*log, nullptr, false);
1776 if (crashdumpJson.is_discarded())
1777 {
1778 messages::internalError(asyncResp->res);
1779 // Careful with onDemandLogMatcher. It is a unique_ptr to
1780 // the match object inside which this lambda is executing.
1781 // Once it is set to nullptr, the match object will be
1782 // destroyed and the lambda will lose its context, including
1783 // res, so it needs to be the last thing done.
1784 onDemandLogMatcher = nullptr;
1785 return;
1786 }
1787 asyncResp->res.jsonValue = crashdumpJson;
Jason M. Bills48e46392019-02-13 12:58:37 -08001788 // Careful with onDemandLogMatcher. It is a unique_ptr to the
Ed Tanous1da66f72018-07-27 16:13:37 -07001789 // match object inside which this lambda is executing. Once it
1790 // is set to nullptr, the match object will be destroyed and the
1791 // lambda will lose its context, including res, so it needs to
1792 // be the last thing done.
Jason M. Bills48e46392019-02-13 12:58:37 -08001793 onDemandLogMatcher = nullptr;
Jason M. Bills3cf8ea32019-10-08 11:43:32 -07001794 };
Jason M. Bills48e46392019-02-13 12:58:37 -08001795 onDemandLogMatcher = std::make_unique<sdbusplus::bus::match::match>(
Ed Tanous1da66f72018-07-27 16:13:37 -07001796 *crow::connections::systemBus,
1797 sdbusplus::bus::match::rules::interfacesAdded() +
Jason M. Bills424c4172019-03-21 13:50:33 -07001798 sdbusplus::bus::match::rules::argNpath(0,
1799 CrashdumpOnDemandPath),
Jason M. Bills48e46392019-02-13 12:58:37 -08001800 std::move(onDemandLogMatcherCallback));
Ed Tanous1da66f72018-07-27 16:13:37 -07001801
Jason M. Bills48e46392019-02-13 12:58:37 -08001802 auto generateonDemandLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -07001803 [asyncResp](const boost::system::error_code ec,
1804 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -07001805 if (ec)
1806 {
1807 if (ec.value() ==
1808 boost::system::errc::operation_not_supported)
1809 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001810 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001811 }
1812 else
1813 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001814 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001815 }
Ed Tanous271584a2019-07-09 16:24:22 -07001816
1817 timeout.cancel();
Jason M. Bills48e46392019-02-13 12:58:37 -08001818 onDemandLogMatcher = nullptr;
Ed Tanous1da66f72018-07-27 16:13:37 -07001819 return;
1820 }
1821 };
1822 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001823 std::move(generateonDemandLogCallback), CrashdumpObject,
1824 CrashdumpPath, CrashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001825 }
1826};
1827
Jason M. Billse1f26342018-07-18 12:12:00 -07001828class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001829{
1830 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001831 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001832 Node(app,
1833 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1834 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001835 {
1836 entityPrivileges = {
1837 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1838 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1839 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1840 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1841 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1842 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1843 }
1844
1845 private:
1846 void doPost(crow::Response &res, const crow::Request &req,
1847 const std::vector<std::string> &params) override
1848 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001849 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanousb1556422018-10-16 14:09:17 -07001850 uint8_t clientAddress = 0;
1851 uint8_t readLength = 0;
Ed Tanous1da66f72018-07-27 16:13:37 -07001852 std::vector<uint8_t> peciCommand;
Ed Tanousb1556422018-10-16 14:09:17 -07001853 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1854 "ReadLength", readLength, "PECICommand",
1855 peciCommand))
Ed Tanous1da66f72018-07-27 16:13:37 -07001856 {
Ed Tanousb1556422018-10-16 14:09:17 -07001857 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001858 }
Ed Tanousb1556422018-10-16 14:09:17 -07001859
Ed Tanous1da66f72018-07-27 16:13:37 -07001860 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001861 auto sendRawPECICallback =
1862 [asyncResp](const boost::system::error_code ec,
1863 const std::vector<uint8_t> &resp) {
1864 if (ec)
1865 {
1866 BMCWEB_LOG_DEBUG << "failed to send PECI command ec: "
1867 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001868 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001869 return;
1870 }
1871 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1872 {"PECIResponse", resp}};
1873 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001874 // Call the SendRawPECI command with the provided data
1875 crow::connections::systemBus->async_method_call(
Jason M. Bills424c4172019-03-21 13:50:33 -07001876 std::move(sendRawPECICallback), CrashdumpObject, CrashdumpPath,
1877 CrashdumpRawPECIInterface, "SendRawPeci", clientAddress, readLength,
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001878 peciCommand);
Ed Tanous1da66f72018-07-27 16:13:37 -07001879 }
1880};
1881
Andrew Geisslercb92c032018-08-17 07:56:14 -07001882/**
1883 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1884 */
1885class DBusLogServiceActionsClear : public Node
1886{
1887 public:
1888 DBusLogServiceActionsClear(CrowApp &app) :
1889 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Tim Lee1f56a3a2019-10-09 10:17:57 +08001890 "LogService.ClearLog")
Andrew Geisslercb92c032018-08-17 07:56:14 -07001891 {
1892 entityPrivileges = {
1893 {boost::beast::http::verb::get, {{"Login"}}},
1894 {boost::beast::http::verb::head, {{"Login"}}},
1895 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1896 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1897 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1898 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1899 }
1900
1901 private:
1902 /**
1903 * Function handles POST method request.
1904 * The Clear Log actions does not require any parameter.The action deletes
1905 * all entries found in the Entries collection for this Log Service.
1906 */
1907 void doPost(crow::Response &res, const crow::Request &req,
1908 const std::vector<std::string> &params) override
1909 {
1910 BMCWEB_LOG_DEBUG << "Do delete all entries.";
1911
1912 auto asyncResp = std::make_shared<AsyncResp>(res);
1913 // Process response from Logging service.
1914 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
1915 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
1916 if (ec)
1917 {
1918 // TODO Handle for specific error code
1919 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
1920 asyncResp->res.result(
1921 boost::beast::http::status::internal_server_error);
1922 return;
1923 }
1924
1925 asyncResp->res.result(boost::beast::http::status::no_content);
1926 };
1927
1928 // Make call to Logging service to request Clear Log
1929 crow::connections::systemBus->async_method_call(
1930 resp_handler, "xyz.openbmc_project.Logging",
1931 "/xyz/openbmc_project/logging",
1932 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1933 }
1934};
Ed Tanous1da66f72018-07-27 16:13:37 -07001935} // namespace redfish