blob: aede3661b35e75ddc5a738af8c929cc1661e3dd0 [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>
Jason M. Bills1ddcf012019-11-26 14:59:21 -080028#include <boost/system/linux_error.hpp>
Andrew Geisslercb92c032018-08-17 07:56:14 -070029#include <error_messages.hpp>
James Feist4418c7f2019-04-15 11:09:15 -070030#include <filesystem>
Jason M. Billscd225da2019-05-08 15:31:57 -070031#include <string_view>
Ed Tanousabf2add2019-01-22 16:40:12 -080032#include <variant>
Ed Tanous1da66f72018-07-27 16:13:37 -070033
34namespace redfish
35{
36
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070037constexpr char const *crashdumpObject = "com.intel.crashdump";
38constexpr char const *crashdumpPath = "/com/intel/crashdump";
39constexpr char const *crashdumpOnDemandPath = "/com/intel/crashdump/OnDemand";
40constexpr char const *crashdumpInterface = "com.intel.crashdump";
41constexpr char const *deleteAllInterface =
42 "xyz.openbmc_project.Collection.DeleteAll";
43constexpr char const *crashdumpOnDemandInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070044 "com.intel.crashdump.OnDemand";
Jason M. Bills5b61b5e2019-10-16 10:59:02 -070045constexpr char const *crashdumpRawPECIInterface =
Jason M. Bills424c4172019-03-21 13:50:33 -070046 "com.intel.crashdump.SendRawPeci";
Ed Tanous1da66f72018-07-27 16:13:37 -070047
Jason M. Bills4851d452019-03-28 11:27:48 -070048namespace message_registries
49{
50static const Message *getMessageFromRegistry(
51 const std::string &messageKey,
52 const boost::beast::span<const MessageEntry> registry)
53{
54 boost::beast::span<const MessageEntry>::const_iterator messageIt =
55 std::find_if(registry.cbegin(), registry.cend(),
56 [&messageKey](const MessageEntry &messageEntry) {
57 return !std::strcmp(messageEntry.first,
58 messageKey.c_str());
59 });
60 if (messageIt != registry.cend())
61 {
62 return &messageIt->second;
63 }
64
65 return nullptr;
66}
67
68static const Message *getMessage(const std::string_view &messageID)
69{
70 // Redfish MessageIds are in the form
71 // RegistryName.MajorVersion.MinorVersion.MessageKey, so parse it to find
72 // the right Message
73 std::vector<std::string> fields;
74 fields.reserve(4);
75 boost::split(fields, messageID, boost::is_any_of("."));
76 std::string &registryName = fields[0];
77 std::string &messageKey = fields[3];
78
79 // Find the right registry and check it for the MessageKey
80 if (std::string(base::header.registryPrefix) == registryName)
81 {
82 return getMessageFromRegistry(
83 messageKey, boost::beast::span<const MessageEntry>(base::registry));
84 }
85 if (std::string(openbmc::header.registryPrefix) == registryName)
86 {
87 return getMessageFromRegistry(
88 messageKey,
89 boost::beast::span<const MessageEntry>(openbmc::registry));
90 }
91 return nullptr;
92}
93} // namespace message_registries
94
James Feistf6150402019-01-08 10:36:20 -080095namespace fs = std::filesystem;
Ed Tanous1da66f72018-07-27 16:13:37 -070096
Andrew Geisslercb92c032018-08-17 07:56:14 -070097using GetManagedPropertyType = boost::container::flat_map<
98 std::string,
99 sdbusplus::message::variant<std::string, bool, uint8_t, int16_t, uint16_t,
100 int32_t, uint32_t, int64_t, uint64_t, double>>;
101
102using GetManagedObjectsType = boost::container::flat_map<
103 sdbusplus::message::object_path,
104 boost::container::flat_map<std::string, GetManagedPropertyType>>;
105
106inline std::string translateSeverityDbusToRedfish(const std::string &s)
107{
108 if (s == "xyz.openbmc_project.Logging.Entry.Level.Alert")
109 {
110 return "Critical";
111 }
112 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Critical")
113 {
114 return "Critical";
115 }
116 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Debug")
117 {
118 return "OK";
119 }
120 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency")
121 {
122 return "Critical";
123 }
124 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Error")
125 {
126 return "Critical";
127 }
128 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Informational")
129 {
130 return "OK";
131 }
132 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Notice")
133 {
134 return "OK";
135 }
136 else if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
137 {
138 return "Warning";
139 }
140 return "";
141}
142
Jason M. Bills16428a12018-11-02 12:42:29 -0700143static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800144 const std::string_view &field,
145 std::string_view &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700146{
147 const char *data = nullptr;
148 size_t length = 0;
149 int ret = 0;
150 // Get the metadata from the requested field of the journal entry
Ed Tanous271584a2019-07-09 16:24:22 -0700151 ret = sd_journal_get_data(journal, field.data(),
152 reinterpret_cast<const void **>(&data), &length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700153 if (ret < 0)
154 {
155 return ret;
156 }
Ed Tanous39e77502019-03-04 17:35:53 -0800157 contents = std::string_view(data, length);
Jason M. Bills16428a12018-11-02 12:42:29 -0700158 // Only use the content after the "=" character.
159 contents.remove_prefix(std::min(contents.find("=") + 1, contents.size()));
160 return ret;
161}
162
163static int getJournalMetadata(sd_journal *journal,
Ed Tanous39e77502019-03-04 17:35:53 -0800164 const std::string_view &field, const int &base,
Ed Tanous271584a2019-07-09 16:24:22 -0700165 long int &contents)
Jason M. Bills16428a12018-11-02 12:42:29 -0700166{
167 int ret = 0;
Ed Tanous39e77502019-03-04 17:35:53 -0800168 std::string_view metadata;
Jason M. Bills16428a12018-11-02 12:42:29 -0700169 // Get the metadata from the requested field of the journal entry
170 ret = getJournalMetadata(journal, field, metadata);
171 if (ret < 0)
172 {
173 return ret;
174 }
Ed Tanousb01bf292019-03-25 19:25:26 +0000175 contents = strtol(metadata.data(), nullptr, base);
Jason M. Bills16428a12018-11-02 12:42:29 -0700176 return ret;
177}
178
179static bool getEntryTimestamp(sd_journal *journal, std::string &entryTimestamp)
180{
181 int ret = 0;
182 uint64_t timestamp = 0;
183 ret = sd_journal_get_realtime_usec(journal, &timestamp);
184 if (ret < 0)
185 {
186 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
187 << strerror(-ret);
188 return false;
189 }
190 time_t t =
191 static_cast<time_t>(timestamp / 1000 / 1000); // Convert from us to s
192 struct tm *loctime = localtime(&t);
193 char entryTime[64] = {};
Ed Tanous99131cd2019-10-24 11:12:47 -0700194 if (nullptr != loctime)
Jason M. Bills16428a12018-11-02 12:42:29 -0700195 {
196 strftime(entryTime, sizeof(entryTime), "%FT%T%z", loctime);
197 }
198 // Insert the ':' into the timezone
Ed Tanous39e77502019-03-04 17:35:53 -0800199 std::string_view t1(entryTime);
200 std::string_view t2(entryTime);
Jason M. Bills16428a12018-11-02 12:42:29 -0700201 if (t1.size() > 2 && t2.size() > 2)
202 {
203 t1.remove_suffix(2);
204 t2.remove_prefix(t2.size() - 2);
205 }
Ed Tanous39e77502019-03-04 17:35:53 -0800206 entryTimestamp = std::string(t1) + ":" + std::string(t2);
Jason M. Bills16428a12018-11-02 12:42:29 -0700207 return true;
208}
209
210static bool getSkipParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700211 uint64_t &skip)
Jason M. Bills16428a12018-11-02 12:42:29 -0700212{
213 char *skipParam = req.urlParams.get("$skip");
214 if (skipParam != nullptr)
215 {
216 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700217 skip = std::strtoul(skipParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700218 if (*skipParam == '\0' || *ptr != '\0')
219 {
220
221 messages::queryParameterValueTypeError(res, std::string(skipParam),
222 "$skip");
223 return false;
224 }
Jason M. Bills16428a12018-11-02 12:42:29 -0700225 }
226 return true;
227}
228
Ed Tanous271584a2019-07-09 16:24:22 -0700229static constexpr const uint64_t maxEntriesPerPage = 1000;
Jason M. Bills16428a12018-11-02 12:42:29 -0700230static bool getTopParam(crow::Response &res, const crow::Request &req,
Ed Tanous271584a2019-07-09 16:24:22 -0700231 uint64_t &top)
Jason M. Bills16428a12018-11-02 12:42:29 -0700232{
233 char *topParam = req.urlParams.get("$top");
234 if (topParam != nullptr)
235 {
236 char *ptr = nullptr;
Ed Tanous271584a2019-07-09 16:24:22 -0700237 top = std::strtoul(topParam, &ptr, 10);
Jason M. Bills16428a12018-11-02 12:42:29 -0700238 if (*topParam == '\0' || *ptr != '\0')
239 {
240 messages::queryParameterValueTypeError(res, std::string(topParam),
241 "$top");
242 return false;
243 }
Ed Tanous271584a2019-07-09 16:24:22 -0700244 if (top < 1U || top > maxEntriesPerPage)
Jason M. Bills16428a12018-11-02 12:42:29 -0700245 {
246
247 messages::queryParameterOutOfRange(
248 res, std::to_string(top), "$top",
249 "1-" + std::to_string(maxEntriesPerPage));
250 return false;
251 }
252 }
253 return true;
254}
255
Jason M. Billse85d6b12019-07-29 17:01:15 -0700256static bool getUniqueEntryID(sd_journal *journal, std::string &entryID,
257 const bool firstEntry = true)
Jason M. Bills16428a12018-11-02 12:42:29 -0700258{
259 int ret = 0;
260 static uint64_t prevTs = 0;
261 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700262 if (firstEntry)
263 {
264 prevTs = 0;
265 }
266
Jason M. Bills16428a12018-11-02 12:42:29 -0700267 // Get the entry timestamp
268 uint64_t curTs = 0;
269 ret = sd_journal_get_realtime_usec(journal, &curTs);
270 if (ret < 0)
271 {
272 BMCWEB_LOG_ERROR << "Failed to read entry timestamp: "
273 << strerror(-ret);
274 return false;
275 }
276 // If the timestamp isn't unique, increment the index
277 if (curTs == prevTs)
278 {
279 index++;
280 }
281 else
282 {
283 // Otherwise, reset it
284 index = 0;
285 }
286 // Save the timestamp
287 prevTs = curTs;
288
289 entryID = std::to_string(curTs);
290 if (index > 0)
291 {
292 entryID += "_" + std::to_string(index);
293 }
294 return true;
295}
296
Jason M. Billse85d6b12019-07-29 17:01:15 -0700297static bool getUniqueEntryID(const std::string &logEntry, std::string &entryID,
298 const bool firstEntry = true)
Jason M. Bills95820182019-04-22 16:25:34 -0700299{
Ed Tanous271584a2019-07-09 16:24:22 -0700300 static time_t prevTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700301 static int index = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700302 if (firstEntry)
303 {
304 prevTs = 0;
305 }
306
Jason M. Bills95820182019-04-22 16:25:34 -0700307 // Get the entry timestamp
Ed Tanous271584a2019-07-09 16:24:22 -0700308 std::time_t curTs = 0;
Jason M. Bills95820182019-04-22 16:25:34 -0700309 std::tm timeStruct = {};
310 std::istringstream entryStream(logEntry);
311 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
312 {
313 curTs = std::mktime(&timeStruct);
314 }
315 // If the timestamp isn't unique, increment the index
316 if (curTs == prevTs)
317 {
318 index++;
319 }
320 else
321 {
322 // Otherwise, reset it
323 index = 0;
324 }
325 // Save the timestamp
326 prevTs = curTs;
327
328 entryID = std::to_string(curTs);
329 if (index > 0)
330 {
331 entryID += "_" + std::to_string(index);
332 }
333 return true;
334}
335
Jason M. Bills16428a12018-11-02 12:42:29 -0700336static bool getTimestampFromID(crow::Response &res, const std::string &entryID,
Ed Tanous271584a2019-07-09 16:24:22 -0700337 uint64_t &timestamp, uint64_t &index)
Jason M. Bills16428a12018-11-02 12:42:29 -0700338{
339 if (entryID.empty())
340 {
341 return false;
342 }
343 // Convert the unique ID back to a timestamp to find the entry
Ed Tanous39e77502019-03-04 17:35:53 -0800344 std::string_view tsStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700345
346 auto underscorePos = tsStr.find("_");
347 if (underscorePos != tsStr.npos)
348 {
349 // Timestamp has an index
350 tsStr.remove_suffix(tsStr.size() - underscorePos);
Ed Tanous39e77502019-03-04 17:35:53 -0800351 std::string_view indexStr(entryID);
Jason M. Bills16428a12018-11-02 12:42:29 -0700352 indexStr.remove_prefix(underscorePos + 1);
353 std::size_t pos;
354 try
355 {
Ed Tanous39e77502019-03-04 17:35:53 -0800356 index = std::stoul(std::string(indexStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700357 }
Ed Tanous271584a2019-07-09 16:24:22 -0700358 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700359 {
360 messages::resourceMissingAtURI(res, entryID);
361 return false;
362 }
Ed Tanous271584a2019-07-09 16:24:22 -0700363 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700364 {
365 messages::resourceMissingAtURI(res, entryID);
366 return false;
367 }
368 if (pos != indexStr.size())
369 {
370 messages::resourceMissingAtURI(res, entryID);
371 return false;
372 }
373 }
374 // Timestamp has no index
375 std::size_t pos;
376 try
377 {
Ed Tanous39e77502019-03-04 17:35:53 -0800378 timestamp = std::stoull(std::string(tsStr), &pos);
Jason M. Bills16428a12018-11-02 12:42:29 -0700379 }
Ed Tanous271584a2019-07-09 16:24:22 -0700380 catch (std::invalid_argument &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700381 {
382 messages::resourceMissingAtURI(res, entryID);
383 return false;
384 }
Ed Tanous271584a2019-07-09 16:24:22 -0700385 catch (std::out_of_range &)
Jason M. Bills16428a12018-11-02 12:42:29 -0700386 {
387 messages::resourceMissingAtURI(res, entryID);
388 return false;
389 }
390 if (pos != tsStr.size())
391 {
392 messages::resourceMissingAtURI(res, entryID);
393 return false;
394 }
395 return true;
396}
397
Jason M. Bills95820182019-04-22 16:25:34 -0700398static bool
399 getRedfishLogFiles(std::vector<std::filesystem::path> &redfishLogFiles)
400{
401 static const std::filesystem::path redfishLogDir = "/var/log";
402 static const std::string redfishLogFilename = "redfish";
403
404 // Loop through the directory looking for redfish log files
405 for (const std::filesystem::directory_entry &dirEnt :
406 std::filesystem::directory_iterator(redfishLogDir))
407 {
408 // If we find a redfish log file, save the path
409 std::string filename = dirEnt.path().filename();
410 if (boost::starts_with(filename, redfishLogFilename))
411 {
412 redfishLogFiles.emplace_back(redfishLogDir / filename);
413 }
414 }
415 // As the log files rotate, they are appended with a ".#" that is higher for
416 // the older logs. Since we don't expect more than 10 log files, we
417 // can just sort the list to get them in order from newest to oldest
418 std::sort(redfishLogFiles.begin(), redfishLogFiles.end());
419
420 return !redfishLogFiles.empty();
421}
422
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800423class SystemLogServiceCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -0700424{
425 public:
426 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800427 SystemLogServiceCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800428 Node(app, "/redfish/v1/Systems/system/LogServices/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800429 {
430 entityPrivileges = {
431 {boost::beast::http::verb::get, {{"Login"}}},
432 {boost::beast::http::verb::head, {{"Login"}}},
433 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
434 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
435 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
436 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
437 }
438
439 private:
440 /**
441 * Functions triggers appropriate requests on DBus
442 */
443 void doGet(crow::Response &res, const crow::Request &req,
444 const std::vector<std::string> &params) override
445 {
446 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800447 // Collections don't include the static data added by SubRoute because
448 // it has a duplicate entry for members
449 asyncResp->res.jsonValue["@odata.type"] =
450 "#LogServiceCollection.LogServiceCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800451 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800452 "/redfish/v1/Systems/system/LogServices";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800453 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
454 asyncResp->res.jsonValue["Description"] =
455 "Collection of LogServices for this Computer System";
456 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
457 logServiceArray = nlohmann::json::array();
Ed Tanous029573d2019-02-01 10:57:49 -0800458 logServiceArray.push_back(
459 {{"@odata.id", "/redfish/v1/Systems/system/LogServices/EventLog"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800460#ifdef BMCWEB_ENABLE_REDFISH_CPU_LOG
461 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500462 {{"@odata.id",
463 "/redfish/v1/Systems/system/LogServices/Crashdump"}});
Jason M. Billsd53dd412019-02-12 17:16:22 -0800464#endif
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800465 asyncResp->res.jsonValue["Members@odata.count"] =
466 logServiceArray.size();
467 }
468};
469
470class EventLogService : public Node
471{
472 public:
473 template <typename CrowApp>
474 EventLogService(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800475 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800476 {
477 entityPrivileges = {
478 {boost::beast::http::verb::get, {{"Login"}}},
479 {boost::beast::http::verb::head, {{"Login"}}},
480 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
481 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
482 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
483 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
484 }
485
486 private:
487 void doGet(crow::Response &res, const crow::Request &req,
488 const std::vector<std::string> &params) override
489 {
490 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
491
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800492 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800493 "/redfish/v1/Systems/system/LogServices/EventLog";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800494 asyncResp->res.jsonValue["@odata.type"] =
495 "#LogService.v1_1_0.LogService";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800496 asyncResp->res.jsonValue["Name"] = "Event Log Service";
497 asyncResp->res.jsonValue["Description"] = "System Event Log Service";
498 asyncResp->res.jsonValue["Id"] = "Event Log";
499 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
500 asyncResp->res.jsonValue["Entries"] = {
501 {"@odata.id",
Ed Tanous029573d2019-02-01 10:57:49 -0800502 "/redfish/v1/Systems/system/LogServices/EventLog/Entries"}};
Gunnar Millse7d6c8b2019-07-03 11:30:01 -0500503 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"] = {
504
505 {"target", "/redfish/v1/Systems/system/LogServices/EventLog/"
506 "Actions/LogService.ClearLog"}};
Jason M. Bills489640c2019-05-17 09:56:36 -0700507 }
508};
509
Tim Lee1f56a3a2019-10-09 10:17:57 +0800510class JournalEventLogClear : public Node
Jason M. Bills489640c2019-05-17 09:56:36 -0700511{
512 public:
Tim Lee1f56a3a2019-10-09 10:17:57 +0800513 JournalEventLogClear(CrowApp &app) :
Jason M. Bills489640c2019-05-17 09:56:36 -0700514 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
515 "LogService.ClearLog/")
516 {
517 entityPrivileges = {
518 {boost::beast::http::verb::get, {{"Login"}}},
519 {boost::beast::http::verb::head, {{"Login"}}},
520 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
521 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
522 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
523 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
524 }
525
526 private:
527 void doPost(crow::Response &res, const crow::Request &req,
528 const std::vector<std::string> &params) override
529 {
530 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
531
532 // Clear the EventLog by deleting the log files
533 std::vector<std::filesystem::path> redfishLogFiles;
534 if (getRedfishLogFiles(redfishLogFiles))
535 {
536 for (const std::filesystem::path &file : redfishLogFiles)
537 {
538 std::error_code ec;
539 std::filesystem::remove(file, ec);
540 }
541 }
542
543 // Reload rsyslog so it knows to start new log files
544 crow::connections::systemBus->async_method_call(
545 [asyncResp](const boost::system::error_code ec) {
546 if (ec)
547 {
548 BMCWEB_LOG_ERROR << "Failed to reload rsyslog: " << ec;
549 messages::internalError(asyncResp->res);
550 return;
551 }
552
553 messages::success(asyncResp->res);
554 },
555 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
556 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
557 "replace");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800558 }
559};
560
Jason M. Bills95820182019-04-22 16:25:34 -0700561static int fillEventLogEntryJson(const std::string &logEntryID,
562 const std::string logEntry,
563 nlohmann::json &logEntryJson)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800564{
Jason M. Bills95820182019-04-22 16:25:34 -0700565 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
Jason M. Billscd225da2019-05-08 15:31:57 -0700566 // First get the Timestamp
567 size_t space = logEntry.find_first_of(" ");
568 if (space == std::string::npos)
Jason M. Bills95820182019-04-22 16:25:34 -0700569 {
570 return 1;
571 }
Jason M. Billscd225da2019-05-08 15:31:57 -0700572 std::string timestamp = logEntry.substr(0, space);
573 // Then get the log contents
574 size_t entryStart = logEntry.find_first_not_of(" ", space);
575 if (entryStart == std::string::npos)
576 {
577 return 1;
578 }
579 std::string_view entry(logEntry);
580 entry.remove_prefix(entryStart);
581 // Use split to separate the entry into its fields
582 std::vector<std::string> logEntryFields;
583 boost::split(logEntryFields, entry, boost::is_any_of(","),
584 boost::token_compress_on);
585 // We need at least a MessageId to be valid
586 if (logEntryFields.size() < 1)
587 {
588 return 1;
589 }
590 std::string &messageID = logEntryFields[0];
Jason M. Bills95820182019-04-22 16:25:34 -0700591
Jason M. Bills4851d452019-03-28 11:27:48 -0700592 // Get the Message from the MessageRegistry
593 const message_registries::Message *message =
594 message_registries::getMessage(messageID);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800595
Jason M. Bills4851d452019-03-28 11:27:48 -0700596 std::string msg;
597 std::string severity;
598 if (message != nullptr)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800599 {
Jason M. Bills4851d452019-03-28 11:27:48 -0700600 msg = message->message;
601 severity = message->severity;
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800602 }
603
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700604 // Get the MessageArgs from the log if there are any
605 boost::beast::span<std::string> messageArgs;
606 if (logEntryFields.size() > 1)
Jason M. Bills4851d452019-03-28 11:27:48 -0700607 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700608 std::string &messageArgsStart = logEntryFields[1];
609 // If the first string is empty, assume there are no MessageArgs
610 std::size_t messageArgsSize = 0;
611 if (!messageArgsStart.empty())
Jason M. Bills4851d452019-03-28 11:27:48 -0700612 {
Jason M. Bills15a86ff2019-06-18 13:49:54 -0700613 messageArgsSize = logEntryFields.size() - 1;
614 }
615
616 messageArgs = boost::beast::span(&messageArgsStart, messageArgsSize);
617
618 // Fill the MessageArgs into the Message
619 int i = 0;
620 for (const std::string &messageArg : messageArgs)
621 {
622 std::string argStr = "%" + std::to_string(++i);
623 size_t argPos = msg.find(argStr);
624 if (argPos != std::string::npos)
625 {
626 msg.replace(argPos, argStr.length(), messageArg);
627 }
Jason M. Bills4851d452019-03-28 11:27:48 -0700628 }
629 }
630
Jason M. Bills95820182019-04-22 16:25:34 -0700631 // Get the Created time from the timestamp. The log timestamp is in RFC3339
632 // format which matches the Redfish format except for the fractional seconds
633 // between the '.' and the '+', so just remove them.
634 std::size_t dot = timestamp.find_first_of(".");
635 std::size_t plus = timestamp.find_first_of("+");
636 if (dot != std::string::npos && plus != std::string::npos)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800637 {
Jason M. Bills95820182019-04-22 16:25:34 -0700638 timestamp.erase(dot, plus - dot);
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800639 }
640
641 // Fill in the log entry with the gathered data
Jason M. Bills95820182019-04-22 16:25:34 -0700642 logEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -0700643 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Ed Tanous029573d2019-02-01 10:57:49 -0800644 {"@odata.id",
Jason M. Bills897967d2019-07-29 17:05:30 -0700645 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/" +
Jason M. Bills95820182019-04-22 16:25:34 -0700646 logEntryID},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800647 {"Name", "System Event Log Entry"},
Jason M. Bills95820182019-04-22 16:25:34 -0700648 {"Id", logEntryID},
649 {"Message", std::move(msg)},
650 {"MessageId", std::move(messageID)},
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800651 {"MessageArgs", std::move(messageArgs)},
652 {"EntryType", "Event"},
Jason M. Bills95820182019-04-22 16:25:34 -0700653 {"Severity", std::move(severity)},
654 {"Created", std::move(timestamp)}};
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800655 return 0;
656}
657
Anthony Wilson27062602019-04-22 02:10:09 -0500658class JournalEventLogEntryCollection : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800659{
660 public:
661 template <typename CrowApp>
Anthony Wilson27062602019-04-22 02:10:09 -0500662 JournalEventLogEntryCollection(CrowApp &app) :
Ed Tanous029573d2019-02-01 10:57:49 -0800663 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800664 {
665 entityPrivileges = {
666 {boost::beast::http::verb::get, {{"Login"}}},
667 {boost::beast::http::verb::head, {{"Login"}}},
668 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
669 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
670 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
671 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
672 }
673
674 private:
675 void doGet(crow::Response &res, const crow::Request &req,
676 const std::vector<std::string> &params) override
677 {
678 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous271584a2019-07-09 16:24:22 -0700679 uint64_t skip = 0;
680 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800681 if (!getSkipParam(asyncResp->res, req, skip))
682 {
683 return;
684 }
685 if (!getTopParam(asyncResp->res, req, top))
686 {
687 return;
688 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800689 // Collections don't include the static data added by SubRoute because
690 // it has a duplicate entry for members
691 asyncResp->res.jsonValue["@odata.type"] =
692 "#LogEntryCollection.LogEntryCollection";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800693 asyncResp->res.jsonValue["@odata.id"] =
Ed Tanous029573d2019-02-01 10:57:49 -0800694 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800695 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
696 asyncResp->res.jsonValue["Description"] =
697 "Collection of System Event Log Entries";
Andrew Geisslercb92c032018-08-17 07:56:14 -0700698
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800699 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
700 logEntryArray = nlohmann::json::array();
Jason M. Bills95820182019-04-22 16:25:34 -0700701 // Go through the log files and create a unique ID for each entry
702 std::vector<std::filesystem::path> redfishLogFiles;
703 getRedfishLogFiles(redfishLogFiles);
Ed Tanousb01bf292019-03-25 19:25:26 +0000704 uint64_t entryCount = 0;
Jason M. Billscd225da2019-05-08 15:31:57 -0700705 std::string logEntry;
Jason M. Bills95820182019-04-22 16:25:34 -0700706
707 // Oldest logs are in the last file, so start there and loop backwards
Jason M. Billscd225da2019-05-08 15:31:57 -0700708 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
709 it++)
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800710 {
Jason M. Billscd225da2019-05-08 15:31:57 -0700711 std::ifstream logStream(*it);
Jason M. Bills95820182019-04-22 16:25:34 -0700712 if (!logStream.is_open())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800713 {
714 continue;
715 }
716
Jason M. Billse85d6b12019-07-29 17:01:15 -0700717 // Reset the unique ID on the first entry
718 bool firstEntry = true;
Jason M. Bills95820182019-04-22 16:25:34 -0700719 while (std::getline(logStream, logEntry))
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800720 {
Jason M. Bills95820182019-04-22 16:25:34 -0700721 entryCount++;
722 // Handle paging using skip (number of entries to skip from the
723 // start) and top (number of entries to display)
724 if (entryCount <= skip || entryCount > skip + top)
725 {
726 continue;
727 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800728
Jason M. Bills95820182019-04-22 16:25:34 -0700729 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -0700730 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
Jason M. Bills95820182019-04-22 16:25:34 -0700731 {
732 continue;
733 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800734
Jason M. Billse85d6b12019-07-29 17:01:15 -0700735 if (firstEntry)
736 {
737 firstEntry = false;
738 }
739
Jason M. Bills95820182019-04-22 16:25:34 -0700740 logEntryArray.push_back({});
741 nlohmann::json &bmcLogEntry = logEntryArray.back();
742 if (fillEventLogEntryJson(idStr, logEntry, bmcLogEntry) != 0)
743 {
744 messages::internalError(asyncResp->res);
745 return;
746 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800747 }
748 }
749 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
750 if (skip + top < entryCount)
751 {
752 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Bills95820182019-04-22 16:25:34 -0700753 "/redfish/v1/Systems/system/LogServices/EventLog/"
754 "Entries?$skip=" +
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800755 std::to_string(skip + top);
756 }
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500757 }
758};
759
Jason M. Bills897967d2019-07-29 17:05:30 -0700760class JournalEventLogEntry : public Node
761{
762 public:
763 JournalEventLogEntry(CrowApp &app) :
764 Node(app,
765 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
766 std::string())
767 {
768 entityPrivileges = {
769 {boost::beast::http::verb::get, {{"Login"}}},
770 {boost::beast::http::verb::head, {{"Login"}}},
771 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
772 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
773 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
774 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
775 }
776
777 private:
778 void doGet(crow::Response &res, const crow::Request &req,
779 const std::vector<std::string> &params) override
780 {
781 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
782 if (params.size() != 1)
783 {
784 messages::internalError(asyncResp->res);
785 return;
786 }
787 const std::string &targetID = params[0];
788
789 // Go through the log files and check the unique ID for each entry to
790 // find the target entry
791 std::vector<std::filesystem::path> redfishLogFiles;
792 getRedfishLogFiles(redfishLogFiles);
793 std::string logEntry;
794
795 // Oldest logs are in the last file, so start there and loop backwards
796 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend();
797 it++)
798 {
799 std::ifstream logStream(*it);
800 if (!logStream.is_open())
801 {
802 continue;
803 }
804
805 // Reset the unique ID on the first entry
806 bool firstEntry = true;
807 while (std::getline(logStream, logEntry))
808 {
809 std::string idStr;
810 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
811 {
812 continue;
813 }
814
815 if (firstEntry)
816 {
817 firstEntry = false;
818 }
819
820 if (idStr == targetID)
821 {
822 if (fillEventLogEntryJson(idStr, logEntry,
823 asyncResp->res.jsonValue) != 0)
824 {
825 messages::internalError(asyncResp->res);
826 return;
827 }
828 return;
829 }
830 }
831 }
832 // Requested ID was not found
833 messages::resourceMissingAtURI(asyncResp->res, targetID);
834 }
835};
836
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500837class DBusEventLogEntryCollection : public Node
838{
839 public:
840 template <typename CrowApp>
841 DBusEventLogEntryCollection(CrowApp &app) :
842 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Entries/")
843 {
844 entityPrivileges = {
845 {boost::beast::http::verb::get, {{"Login"}}},
846 {boost::beast::http::verb::head, {{"Login"}}},
847 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
848 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
849 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
850 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
851 }
852
853 private:
854 void doGet(crow::Response &res, const crow::Request &req,
855 const std::vector<std::string> &params) override
856 {
857 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
858
859 // Collections don't include the static data added by SubRoute because
860 // it has a duplicate entry for members
861 asyncResp->res.jsonValue["@odata.type"] =
862 "#LogEntryCollection.LogEntryCollection";
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500863 asyncResp->res.jsonValue["@odata.id"] =
864 "/redfish/v1/Systems/system/LogServices/EventLog/Entries";
865 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
866 asyncResp->res.jsonValue["Description"] =
867 "Collection of System Event Log Entries";
868
Andrew Geisslercb92c032018-08-17 07:56:14 -0700869 // DBus implementation of EventLog/Entries
870 // Make call to Logging Service to find all log entry objects
871 crow::connections::systemBus->async_method_call(
872 [asyncResp](const boost::system::error_code ec,
873 GetManagedObjectsType &resp) {
874 if (ec)
875 {
876 // TODO Handle for specific error code
877 BMCWEB_LOG_ERROR
878 << "getLogEntriesIfaceData resp_handler got error "
879 << ec;
880 messages::internalError(asyncResp->res);
881 return;
882 }
883 nlohmann::json &entriesArray =
884 asyncResp->res.jsonValue["Members"];
885 entriesArray = nlohmann::json::array();
886 for (auto &objectPath : resp)
887 {
888 for (auto &interfaceMap : objectPath.second)
889 {
890 if (interfaceMap.first !=
891 "xyz.openbmc_project.Logging.Entry")
892 {
893 BMCWEB_LOG_DEBUG << "Bailing early on "
894 << interfaceMap.first;
895 continue;
896 }
897 entriesArray.push_back({});
898 nlohmann::json &thisEntry = entriesArray.back();
Ed Tanous66664f22019-10-11 13:05:49 -0700899 uint32_t *id = nullptr;
900 std::time_t timestamp{};
901 std::string *severity = nullptr;
902 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700903 for (auto &propertyMap : interfaceMap.second)
904 {
905 if (propertyMap.first == "Id")
906 {
907 id = sdbusplus::message::variant_ns::get_if<
908 uint32_t>(&propertyMap.second);
909 if (id == nullptr)
910 {
911 messages::propertyMissing(asyncResp->res,
912 "Id");
913 }
914 }
915 else if (propertyMap.first == "Timestamp")
916 {
917 const uint64_t *millisTimeStamp =
918 std::get_if<uint64_t>(&propertyMap.second);
919 if (millisTimeStamp == nullptr)
920 {
921 messages::propertyMissing(asyncResp->res,
922 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -0700923 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -0700924 }
925 // Retrieve Created property with format:
926 // yyyy-mm-ddThh:mm:ss
927 std::chrono::milliseconds chronoTimeStamp(
928 *millisTimeStamp);
Ed Tanous271584a2019-07-09 16:24:22 -0700929 timestamp = std::chrono::duration_cast<
930 std::chrono::duration<int>>(
931 chronoTimeStamp)
932 .count();
Andrew Geisslercb92c032018-08-17 07:56:14 -0700933 }
934 else if (propertyMap.first == "Severity")
935 {
936 severity = std::get_if<std::string>(
937 &propertyMap.second);
938 if (severity == nullptr)
939 {
940 messages::propertyMissing(asyncResp->res,
941 "Severity");
942 }
943 }
944 else if (propertyMap.first == "Message")
945 {
946 message = std::get_if<std::string>(
947 &propertyMap.second);
948 if (message == nullptr)
949 {
950 messages::propertyMissing(asyncResp->res,
951 "Message");
952 }
953 }
954 }
955 thisEntry = {
956 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700957 {"@odata.id",
958 "/redfish/v1/Systems/system/LogServices/EventLog/"
959 "Entries/" +
960 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -0500961 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -0700962 {"Id", std::to_string(*id)},
963 {"Message", *message},
964 {"EntryType", "Event"},
965 {"Severity",
966 translateSeverityDbusToRedfish(*severity)},
967 {"Created", crow::utility::getDateTime(timestamp)}};
968 }
969 }
970 std::sort(entriesArray.begin(), entriesArray.end(),
971 [](const nlohmann::json &left,
972 const nlohmann::json &right) {
973 return (left["Id"] <= right["Id"]);
974 });
975 asyncResp->res.jsonValue["Members@odata.count"] =
976 entriesArray.size();
977 },
978 "xyz.openbmc_project.Logging", "/xyz/openbmc_project/logging",
979 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects");
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800980 }
981};
982
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500983class DBusEventLogEntry : public Node
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800984{
985 public:
Anthony Wilson08a4e4b2019-04-12 08:23:05 -0500986 DBusEventLogEntry(CrowApp &app) :
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800987 Node(app,
Ed Tanous029573d2019-02-01 10:57:49 -0800988 "/redfish/v1/Systems/system/LogServices/EventLog/Entries/<str>/",
989 std::string())
Jason M. Billsc4bf6372018-11-05 13:48:27 -0800990 {
991 entityPrivileges = {
992 {boost::beast::http::verb::get, {{"Login"}}},
993 {boost::beast::http::verb::head, {{"Login"}}},
994 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
995 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
996 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
997 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
998 }
999
1000 private:
1001 void doGet(crow::Response &res, const crow::Request &req,
1002 const std::vector<std::string> &params) override
1003 {
1004 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous029573d2019-02-01 10:57:49 -08001005 if (params.size() != 1)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001006 {
1007 messages::internalError(asyncResp->res);
1008 return;
1009 }
Ed Tanous029573d2019-02-01 10:57:49 -08001010 const std::string &entryID = params[0];
Andrew Geisslercb92c032018-08-17 07:56:14 -07001011
Andrew Geisslercb92c032018-08-17 07:56:14 -07001012 // DBus implementation of EventLog/Entries
1013 // Make call to Logging Service to find all log entry objects
1014 crow::connections::systemBus->async_method_call(
1015 [asyncResp, entryID](const boost::system::error_code ec,
1016 GetManagedPropertyType &resp) {
1017 if (ec)
1018 {
1019 BMCWEB_LOG_ERROR
1020 << "EventLogEntry (DBus) resp_handler got error " << ec;
1021 messages::internalError(asyncResp->res);
1022 return;
1023 }
Ed Tanous66664f22019-10-11 13:05:49 -07001024 uint32_t *id = nullptr;
1025 std::time_t timestamp{};
1026 std::string *severity = nullptr;
1027 std::string *message = nullptr;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001028 for (auto &propertyMap : resp)
1029 {
1030 if (propertyMap.first == "Id")
1031 {
1032 id = std::get_if<uint32_t>(&propertyMap.second);
1033 if (id == nullptr)
1034 {
1035 messages::propertyMissing(asyncResp->res, "Id");
1036 }
1037 }
1038 else if (propertyMap.first == "Timestamp")
1039 {
1040 const uint64_t *millisTimeStamp =
1041 std::get_if<uint64_t>(&propertyMap.second);
1042 if (millisTimeStamp == nullptr)
1043 {
1044 messages::propertyMissing(asyncResp->res,
1045 "Timestamp");
Ed Tanous271584a2019-07-09 16:24:22 -07001046 continue;
Andrew Geisslercb92c032018-08-17 07:56:14 -07001047 }
1048 // Retrieve Created property with format:
1049 // yyyy-mm-ddThh:mm:ss
1050 std::chrono::milliseconds chronoTimeStamp(
1051 *millisTimeStamp);
1052 timestamp =
Ed Tanous271584a2019-07-09 16:24:22 -07001053 std::chrono::duration_cast<
1054 std::chrono::duration<int>>(chronoTimeStamp)
Andrew Geisslercb92c032018-08-17 07:56:14 -07001055 .count();
1056 }
1057 else if (propertyMap.first == "Severity")
1058 {
1059 severity =
1060 std::get_if<std::string>(&propertyMap.second);
1061 if (severity == nullptr)
1062 {
1063 messages::propertyMissing(asyncResp->res,
1064 "Severity");
1065 }
1066 }
1067 else if (propertyMap.first == "Message")
1068 {
1069 message = std::get_if<std::string>(&propertyMap.second);
1070 if (message == nullptr)
1071 {
1072 messages::propertyMissing(asyncResp->res,
1073 "Message");
1074 }
1075 }
1076 }
Ed Tanous271584a2019-07-09 16:24:22 -07001077 if (id == nullptr || message == nullptr || severity == nullptr)
1078 {
1079 return;
1080 }
Andrew Geisslercb92c032018-08-17 07:56:14 -07001081 asyncResp->res.jsonValue = {
1082 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001083 {"@odata.id",
1084 "/redfish/v1/Systems/system/LogServices/EventLog/"
1085 "Entries/" +
1086 std::to_string(*id)},
Anthony Wilson27062602019-04-22 02:10:09 -05001087 {"Name", "System Event Log Entry"},
Andrew Geisslercb92c032018-08-17 07:56:14 -07001088 {"Id", std::to_string(*id)},
1089 {"Message", *message},
1090 {"EntryType", "Event"},
1091 {"Severity", translateSeverityDbusToRedfish(*severity)},
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001092 {"Created", crow::utility::getDateTime(timestamp)}};
Andrew Geisslercb92c032018-08-17 07:56:14 -07001093 },
1094 "xyz.openbmc_project.Logging",
1095 "/xyz/openbmc_project/logging/entry/" + entryID,
1096 "org.freedesktop.DBus.Properties", "GetAll",
1097 "xyz.openbmc_project.Logging.Entry");
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001098 }
Chicago Duan336e96c2019-07-15 14:22:08 +08001099
1100 void doDelete(crow::Response &res, const crow::Request &req,
1101 const std::vector<std::string> &params) override
1102 {
1103
1104 BMCWEB_LOG_DEBUG << "Do delete single event entries.";
1105
1106 auto asyncResp = std::make_shared<AsyncResp>(res);
1107
1108 if (params.size() != 1)
1109 {
1110 messages::internalError(asyncResp->res);
1111 return;
1112 }
1113 std::string entryID = params[0];
1114
1115 dbus::utility::escapePathForDbus(entryID);
1116
1117 // Process response from Logging service.
1118 auto respHandler = [asyncResp](const boost::system::error_code ec) {
1119 BMCWEB_LOG_DEBUG << "EventLogEntry (DBus) doDelete callback: Done";
1120 if (ec)
1121 {
1122 // TODO Handle for specific error code
1123 BMCWEB_LOG_ERROR
1124 << "EventLogEntry (DBus) doDelete respHandler got error "
1125 << ec;
1126 asyncResp->res.result(
1127 boost::beast::http::status::internal_server_error);
1128 return;
1129 }
1130
1131 asyncResp->res.result(boost::beast::http::status::ok);
1132 };
1133
1134 // Make call to Logging service to request Delete Log
1135 crow::connections::systemBus->async_method_call(
1136 respHandler, "xyz.openbmc_project.Logging",
1137 "/xyz/openbmc_project/logging/entry/" + entryID,
1138 "xyz.openbmc_project.Object.Delete", "Delete");
1139 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001140};
1141
1142class BMCLogServiceCollection : public Node
1143{
1144 public:
1145 template <typename CrowApp>
1146 BMCLogServiceCollection(CrowApp &app) :
Ed Tanous4ed77cd2018-10-15 08:08:07 -07001147 Node(app, "/redfish/v1/Managers/bmc/LogServices/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001148 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001149 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001150 {boost::beast::http::verb::get, {{"Login"}}},
1151 {boost::beast::http::verb::head, {{"Login"}}},
1152 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1153 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1154 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1155 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001156 }
1157
1158 private:
1159 /**
1160 * Functions triggers appropriate requests on DBus
1161 */
1162 void doGet(crow::Response &res, const crow::Request &req,
1163 const std::vector<std::string> &params) override
1164 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001165 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001166 // Collections don't include the static data added by SubRoute because
1167 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001168 asyncResp->res.jsonValue["@odata.type"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001169 "#LogServiceCollection.LogServiceCollection";
Jason M. Billse1f26342018-07-18 12:12:00 -07001170 asyncResp->res.jsonValue["@odata.id"] =
1171 "/redfish/v1/Managers/bmc/LogServices";
1172 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1173 asyncResp->res.jsonValue["Description"] =
Ed Tanous1da66f72018-07-27 16:13:37 -07001174 "Collection of LogServices for this Manager";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001175 nlohmann::json &logServiceArray = asyncResp->res.jsonValue["Members"];
1176 logServiceArray = nlohmann::json::array();
1177#ifdef BMCWEB_ENABLE_REDFISH_BMC_JOURNAL
1178 logServiceArray.push_back(
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001179 {{"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal"}});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001180#endif
Jason M. Billse1f26342018-07-18 12:12:00 -07001181 asyncResp->res.jsonValue["Members@odata.count"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001182 logServiceArray.size();
Ed Tanous1da66f72018-07-27 16:13:37 -07001183 }
1184};
1185
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001186class BMCJournalLogService : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001187{
1188 public:
1189 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001190 BMCJournalLogService(CrowApp &app) :
1191 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001192 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001193 entityPrivileges = {
1194 {boost::beast::http::verb::get, {{"Login"}}},
1195 {boost::beast::http::verb::head, {{"Login"}}},
1196 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1197 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1198 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1199 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1200 }
1201
1202 private:
1203 void doGet(crow::Response &res, const crow::Request &req,
1204 const std::vector<std::string> &params) override
1205 {
1206 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001207 asyncResp->res.jsonValue["@odata.type"] =
1208 "#LogService.v1_1_0.LogService";
Ed Tanous0f74e642018-11-12 15:17:05 -08001209 asyncResp->res.jsonValue["@odata.id"] =
1210 "/redfish/v1/Managers/bmc/LogServices/Journal";
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001211 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Log Service";
1212 asyncResp->res.jsonValue["Description"] = "BMC Journal Log Service";
1213 asyncResp->res.jsonValue["Id"] = "BMC Journal";
Jason M. Billse1f26342018-07-18 12:12:00 -07001214 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
Jason M. Billscd50aa42019-02-12 17:09:02 -08001215 asyncResp->res.jsonValue["Entries"] = {
1216 {"@odata.id",
Ed Tanous086be232019-05-23 11:47:09 -07001217 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001218 }
1219};
1220
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001221static int fillBMCJournalLogEntryJson(const std::string &bmcJournalLogEntryID,
1222 sd_journal *journal,
1223 nlohmann::json &bmcJournalLogEntryJson)
Jason M. Billse1f26342018-07-18 12:12:00 -07001224{
1225 // Get the Log Entry contents
1226 int ret = 0;
Jason M. Billse1f26342018-07-18 12:12:00 -07001227
Ed Tanous39e77502019-03-04 17:35:53 -08001228 std::string_view msg;
Jason M. Bills16428a12018-11-02 12:42:29 -07001229 ret = getJournalMetadata(journal, "MESSAGE", msg);
Jason M. Billse1f26342018-07-18 12:12:00 -07001230 if (ret < 0)
1231 {
1232 BMCWEB_LOG_ERROR << "Failed to read MESSAGE field: " << strerror(-ret);
1233 return 1;
1234 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001235
1236 // Get the severity from the PRIORITY field
Ed Tanous271584a2019-07-09 16:24:22 -07001237 long int severity = 8; // Default to an invalid priority
Jason M. Bills16428a12018-11-02 12:42:29 -07001238 ret = getJournalMetadata(journal, "PRIORITY", 10, severity);
Jason M. Billse1f26342018-07-18 12:12:00 -07001239 if (ret < 0)
1240 {
1241 BMCWEB_LOG_ERROR << "Failed to read PRIORITY field: " << strerror(-ret);
Jason M. Billse1f26342018-07-18 12:12:00 -07001242 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001243
1244 // Get the Created time from the timestamp
Jason M. Bills16428a12018-11-02 12:42:29 -07001245 std::string entryTimeStr;
1246 if (!getEntryTimestamp(journal, entryTimeStr))
Jason M. Billse1f26342018-07-18 12:12:00 -07001247 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001248 return 1;
Jason M. Billse1f26342018-07-18 12:12:00 -07001249 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001250
1251 // Fill in the log entry with the gathered data
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001252 bmcJournalLogEntryJson = {
Andrew Geisslercb92c032018-08-17 07:56:14 -07001253 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001254 {"@odata.id", "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/" +
1255 bmcJournalLogEntryID},
Jason M. Billse1f26342018-07-18 12:12:00 -07001256 {"Name", "BMC Journal Entry"},
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001257 {"Id", bmcJournalLogEntryID},
Jason M. Bills16428a12018-11-02 12:42:29 -07001258 {"Message", msg},
Jason M. Billse1f26342018-07-18 12:12:00 -07001259 {"EntryType", "Oem"},
1260 {"Severity",
Jason M. Billsb6a61a52019-08-01 14:26:15 -07001261 severity <= 2 ? "Critical" : severity <= 4 ? "Warning" : "OK"},
Ed Tanous086be232019-05-23 11:47:09 -07001262 {"OemRecordFormat", "BMC Journal Entry"},
Jason M. Billse1f26342018-07-18 12:12:00 -07001263 {"Created", std::move(entryTimeStr)}};
1264 return 0;
1265}
1266
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001267class BMCJournalLogEntryCollection : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001268{
1269 public:
1270 template <typename CrowApp>
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001271 BMCJournalLogEntryCollection(CrowApp &app) :
1272 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/")
Jason M. Billse1f26342018-07-18 12:12:00 -07001273 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001274 entityPrivileges = {
1275 {boost::beast::http::verb::get, {{"Login"}}},
1276 {boost::beast::http::verb::head, {{"Login"}}},
1277 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1278 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1279 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1280 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1281 }
1282
1283 private:
1284 void doGet(crow::Response &res, const crow::Request &req,
1285 const std::vector<std::string> &params) override
1286 {
1287 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001288 static constexpr const long maxEntriesPerPage = 1000;
Ed Tanous271584a2019-07-09 16:24:22 -07001289 uint64_t skip = 0;
1290 uint64_t top = maxEntriesPerPage; // Show max entries by default
Jason M. Bills16428a12018-11-02 12:42:29 -07001291 if (!getSkipParam(asyncResp->res, req, skip))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001292 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001293 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001294 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001295 if (!getTopParam(asyncResp->res, req, top))
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001296 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001297 return;
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001298 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001299 // Collections don't include the static data added by SubRoute because
1300 // it has a duplicate entry for members
1301 asyncResp->res.jsonValue["@odata.type"] =
1302 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001303 asyncResp->res.jsonValue["@odata.id"] =
1304 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001305 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001306 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001307 asyncResp->res.jsonValue["Name"] = "Open BMC Journal Entries";
1308 asyncResp->res.jsonValue["Description"] =
1309 "Collection of BMC Journal Entries";
Ed Tanous0f74e642018-11-12 15:17:05 -08001310 asyncResp->res.jsonValue["@odata.id"] =
1311 "/redfish/v1/Managers/bmc/LogServices/BmcLog/Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001312 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1313 logEntryArray = nlohmann::json::array();
1314
1315 // Go through the journal and use the timestamp to create a unique ID
1316 // for each entry
1317 sd_journal *journalTmp = nullptr;
1318 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1319 if (ret < 0)
1320 {
1321 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001322 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001323 return;
1324 }
1325 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1326 journalTmp, sd_journal_close);
1327 journalTmp = nullptr;
Ed Tanousb01bf292019-03-25 19:25:26 +00001328 uint64_t entryCount = 0;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001329 // Reset the unique ID on the first entry
1330 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001331 SD_JOURNAL_FOREACH(journal.get())
1332 {
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001333 entryCount++;
1334 // Handle paging using skip (number of entries to skip from the
1335 // start) and top (number of entries to display)
1336 if (entryCount <= skip || entryCount > skip + top)
1337 {
1338 continue;
1339 }
1340
Jason M. Bills16428a12018-11-02 12:42:29 -07001341 std::string idStr;
Jason M. Billse85d6b12019-07-29 17:01:15 -07001342 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
Jason M. Billse1f26342018-07-18 12:12:00 -07001343 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001344 continue;
1345 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001346
Jason M. Billse85d6b12019-07-29 17:01:15 -07001347 if (firstEntry)
1348 {
1349 firstEntry = false;
1350 }
1351
Jason M. Billse1f26342018-07-18 12:12:00 -07001352 logEntryArray.push_back({});
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001353 nlohmann::json &bmcJournalLogEntry = logEntryArray.back();
1354 if (fillBMCJournalLogEntryJson(idStr, journal.get(),
1355 bmcJournalLogEntry) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001356 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001357 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001358 return;
1359 }
1360 }
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001361 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1362 if (skip + top < entryCount)
1363 {
1364 asyncResp->res.jsonValue["Members@odata.nextLink"] =
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001365 "/redfish/v1/Managers/bmc/LogServices/Journal/Entries?$skip=" +
Jason M. Bills193ad2f2018-09-26 15:08:52 -07001366 std::to_string(skip + top);
1367 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001368 }
1369};
1370
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001371class BMCJournalLogEntry : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001372{
1373 public:
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001374 BMCJournalLogEntry(CrowApp &app) :
1375 Node(app, "/redfish/v1/Managers/bmc/LogServices/Journal/Entries/<str>/",
Jason M. Billse1f26342018-07-18 12:12:00 -07001376 std::string())
1377 {
1378 entityPrivileges = {
1379 {boost::beast::http::verb::get, {{"Login"}}},
1380 {boost::beast::http::verb::head, {{"Login"}}},
1381 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1382 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1383 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1384 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1385 }
1386
1387 private:
1388 void doGet(crow::Response &res, const crow::Request &req,
1389 const std::vector<std::string> &params) override
1390 {
1391 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1392 if (params.size() != 1)
1393 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001394 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001395 return;
1396 }
Jason M. Bills16428a12018-11-02 12:42:29 -07001397 const std::string &entryID = params[0];
Jason M. Billse1f26342018-07-18 12:12:00 -07001398 // Convert the unique ID back to a timestamp to find the entry
Jason M. Billse1f26342018-07-18 12:12:00 -07001399 uint64_t ts = 0;
Ed Tanous271584a2019-07-09 16:24:22 -07001400 uint64_t index = 0;
Jason M. Bills16428a12018-11-02 12:42:29 -07001401 if (!getTimestampFromID(asyncResp->res, entryID, ts, index))
Jason M. Billse1f26342018-07-18 12:12:00 -07001402 {
Jason M. Bills16428a12018-11-02 12:42:29 -07001403 return;
Jason M. Billse1f26342018-07-18 12:12:00 -07001404 }
1405
1406 sd_journal *journalTmp = nullptr;
1407 int ret = sd_journal_open(&journalTmp, SD_JOURNAL_LOCAL_ONLY);
1408 if (ret < 0)
1409 {
1410 BMCWEB_LOG_ERROR << "failed to open journal: " << strerror(-ret);
Jason M. Billsf12894f2018-10-09 12:45:45 -07001411 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001412 return;
1413 }
1414 std::unique_ptr<sd_journal, decltype(&sd_journal_close)> journal(
1415 journalTmp, sd_journal_close);
1416 journalTmp = nullptr;
1417 // Go to the timestamp in the log and move to the entry at the index
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001418 // tracking the unique ID
1419 std::string idStr;
1420 bool firstEntry = true;
Jason M. Billse1f26342018-07-18 12:12:00 -07001421 ret = sd_journal_seek_realtime_usec(journal.get(), ts);
Ed Tanous271584a2019-07-09 16:24:22 -07001422 for (uint64_t i = 0; i <= index; i++)
Jason M. Billse1f26342018-07-18 12:12:00 -07001423 {
1424 sd_journal_next(journal.get());
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001425 if (!getUniqueEntryID(journal.get(), idStr, firstEntry))
1426 {
1427 messages::internalError(asyncResp->res);
1428 return;
1429 }
1430 if (firstEntry)
1431 {
1432 firstEntry = false;
1433 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001434 }
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001435 // Confirm that the entry ID matches what was requested
Jason M. Billsaf07e3f2019-08-01 14:41:39 -07001436 if (idStr != entryID)
Jason M. Billsc4bf6372018-11-05 13:48:27 -08001437 {
1438 messages::resourceMissingAtURI(asyncResp->res, entryID);
1439 return;
1440 }
1441
1442 if (fillBMCJournalLogEntryJson(entryID, journal.get(),
1443 asyncResp->res.jsonValue) != 0)
Jason M. Billse1f26342018-07-18 12:12:00 -07001444 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001445 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001446 return;
1447 }
1448 }
1449};
1450
Jason M. Bills424c4172019-03-21 13:50:33 -07001451class CrashdumpService : public Node
Jason M. Billse1f26342018-07-18 12:12:00 -07001452{
1453 public:
1454 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001455 CrashdumpService(CrowApp &app) :
1456 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001457 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001458 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001459 {boost::beast::http::verb::get, {{"Login"}}},
1460 {boost::beast::http::verb::head, {{"Login"}}},
1461 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1462 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1463 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1464 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001465 }
1466
1467 private:
1468 /**
1469 * Functions triggers appropriate requests on DBus
1470 */
1471 void doGet(crow::Response &res, const crow::Request &req,
1472 const std::vector<std::string> &params) override
1473 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001474 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001475 // Copy over the static data to include the entries added by SubRoute
Ed Tanous0f74e642018-11-12 15:17:05 -08001476 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001477 "/redfish/v1/Systems/system/LogServices/Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001478 asyncResp->res.jsonValue["@odata.type"] =
1479 "#LogService.v1_1_0.LogService";
Gunnar Mills4f50ae42020-02-06 15:29:57 -06001480 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1481 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1482 asyncResp->res.jsonValue["Id"] = "Oem Crashdump";
Jason M. Billse1f26342018-07-18 12:12:00 -07001483 asyncResp->res.jsonValue["OverWritePolicy"] = "WrapsWhenFull";
1484 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
Jason M. Billscd50aa42019-02-12 17:09:02 -08001485 asyncResp->res.jsonValue["Entries"] = {
1486 {"@odata.id",
Jason M. Bills424c4172019-03-21 13:50:33 -07001487 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries"}};
Jason M. Billse1f26342018-07-18 12:12:00 -07001488 asyncResp->res.jsonValue["Actions"] = {
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001489 {"#LogService.ClearLog",
1490 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1491 "Actions/LogService.ClearLog"}}},
Ed Tanous1da66f72018-07-27 16:13:37 -07001492 {"Oem",
Jason M. Bills424c4172019-03-21 13:50:33 -07001493 {{"#Crashdump.OnDemand",
1494 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1495 "Actions/Oem/Crashdump.OnDemand"}}}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001496
1497#ifdef BMCWEB_ENABLE_REDFISH_RAW_PECI
Jason M. Billse1f26342018-07-18 12:12:00 -07001498 asyncResp->res.jsonValue["Actions"]["Oem"].push_back(
Jason M. Bills424c4172019-03-21 13:50:33 -07001499 {"#Crashdump.SendRawPeci",
Anthony Wilson08a4e4b2019-04-12 08:23:05 -05001500 {{"target", "/redfish/v1/Systems/system/LogServices/Crashdump/"
1501 "Actions/Oem/Crashdump.SendRawPeci"}}});
Ed Tanous1da66f72018-07-27 16:13:37 -07001502#endif
Ed Tanous1da66f72018-07-27 16:13:37 -07001503 }
1504};
1505
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001506class CrashdumpClear : public Node
1507{
1508 public:
1509 CrashdumpClear(CrowApp &app) :
1510 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/"
1511 "LogService.ClearLog/")
1512 {
1513 entityPrivileges = {
1514 {boost::beast::http::verb::get, {{"Login"}}},
1515 {boost::beast::http::verb::head, {{"Login"}}},
1516 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1517 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1518 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1519 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1520 }
1521
1522 private:
1523 void doPost(crow::Response &res, const crow::Request &req,
1524 const std::vector<std::string> &params) override
1525 {
1526 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1527
1528 crow::connections::systemBus->async_method_call(
1529 [asyncResp](const boost::system::error_code ec,
1530 const std::string &resp) {
1531 if (ec)
1532 {
1533 messages::internalError(asyncResp->res);
1534 return;
1535 }
1536 messages::success(asyncResp->res);
1537 },
1538 crashdumpObject, crashdumpPath, deleteAllInterface, "DeleteAll");
1539 }
1540};
1541
Jason M. Billse855dd22019-10-08 11:37:48 -07001542std::string getLogCreatedTime(const std::string &crashdump)
1543{
1544 nlohmann::json crashdumpJson =
1545 nlohmann::json::parse(crashdump, nullptr, false);
1546 if (crashdumpJson.is_discarded())
1547 {
1548 return std::string();
1549 }
1550
1551 nlohmann::json::const_iterator cdIt = crashdumpJson.find("crash_data");
1552 if (cdIt == crashdumpJson.end())
1553 {
1554 return std::string();
1555 }
1556
1557 nlohmann::json::const_iterator siIt = cdIt->find("METADATA");
1558 if (siIt == cdIt->end())
1559 {
1560 return std::string();
1561 }
1562
1563 nlohmann::json::const_iterator tsIt = siIt->find("timestamp");
1564 if (tsIt == siIt->end())
1565 {
1566 return std::string();
1567 }
1568
1569 const std::string *logTime = tsIt->get_ptr<const std::string *>();
1570 if (logTime == nullptr)
1571 {
1572 return std::string();
1573 }
1574
1575 std::string redfishDateTime = *logTime;
1576 if (redfishDateTime.length() > 2)
1577 {
1578 redfishDateTime.insert(redfishDateTime.end() - 2, ':');
1579 }
1580
1581 return redfishDateTime;
1582}
1583
1584std::string getLogFileName(const std::string &logTime)
1585{
1586 // Set the crashdump file name to "crashdump_<logTime>.json" using the
1587 // created time without the timezone info
1588 std::string fileTime = logTime;
1589 size_t plusPos = fileTime.rfind('+');
1590 if (plusPos != std::string::npos)
1591 {
1592 fileTime.erase(plusPos);
1593 }
1594 return "crashdump_" + fileTime + ".json";
1595}
1596
1597static void logCrashdumpEntry(std::shared_ptr<AsyncResp> asyncResp,
1598 const std::string &logID,
1599 nlohmann::json &logEntryJson)
1600{
1601 auto getStoredLogCallback = [asyncResp, logID, &logEntryJson](
1602 const boost::system::error_code ec,
1603 const std::variant<std::string> &resp) {
1604 if (ec)
1605 {
1606 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
Jason M. Bills1ddcf012019-11-26 14:59:21 -08001607 if (ec.value() ==
1608 boost::system::linux_error::bad_request_descriptor)
1609 {
1610 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
1611 }
1612 else
1613 {
1614 messages::internalError(asyncResp->res);
1615 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001616 return;
1617 }
1618 const std::string *log = std::get_if<std::string>(&resp);
1619 if (log == nullptr)
1620 {
1621 messages::internalError(asyncResp->res);
1622 return;
1623 }
1624 std::string logTime = getLogCreatedTime(*log);
1625 std::string fileName = getLogFileName(logTime);
1626
1627 logEntryJson = {
1628 {"@odata.type", "#LogEntry.v1_4_0.LogEntry"},
Jason M. Billse855dd22019-10-08 11:37:48 -07001629 {"@odata.id",
1630 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1631 logID},
1632 {"Name", "CPU Crashdump"},
1633 {"Id", logID},
1634 {"EntryType", "Oem"},
1635 {"OemRecordFormat", "Crashdump URI"},
1636 {"Message",
1637 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/" +
1638 logID + "/" + fileName},
1639 {"Created", std::move(logTime)}};
1640 };
1641 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001642 std::move(getStoredLogCallback), crashdumpObject,
1643 crashdumpPath + std::string("/") + logID,
1644 "org.freedesktop.DBus.Properties", "Get", crashdumpInterface, "Log");
Jason M. Billse855dd22019-10-08 11:37:48 -07001645}
1646
Jason M. Bills424c4172019-03-21 13:50:33 -07001647class CrashdumpEntryCollection : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001648{
1649 public:
1650 template <typename CrowApp>
Jason M. Bills424c4172019-03-21 13:50:33 -07001651 CrashdumpEntryCollection(CrowApp &app) :
1652 Node(app, "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001653 {
Ed Tanous1da66f72018-07-27 16:13:37 -07001654 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001655 {boost::beast::http::verb::get, {{"Login"}}},
1656 {boost::beast::http::verb::head, {{"Login"}}},
1657 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1658 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1659 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1660 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001661 }
1662
1663 private:
1664 /**
1665 * Functions triggers appropriate requests on DBus
1666 */
1667 void doGet(crow::Response &res, const crow::Request &req,
1668 const std::vector<std::string> &params) override
1669 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001670 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001671 // Collections don't include the static data added by SubRoute because
1672 // it has a duplicate entry for members
Jason M. Billse1f26342018-07-18 12:12:00 -07001673 auto getLogEntriesCallback = [asyncResp](
1674 const boost::system::error_code ec,
1675 const std::vector<std::string> &resp) {
1676 if (ec)
1677 {
1678 if (ec.value() !=
1679 boost::system::errc::no_such_file_or_directory)
Ed Tanous1da66f72018-07-27 16:13:37 -07001680 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001681 BMCWEB_LOG_DEBUG << "failed to get entries ec: "
1682 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001683 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001684 return;
Ed Tanous1da66f72018-07-27 16:13:37 -07001685 }
Jason M. Billse1f26342018-07-18 12:12:00 -07001686 }
1687 asyncResp->res.jsonValue["@odata.type"] =
1688 "#LogEntryCollection.LogEntryCollection";
Ed Tanous0f74e642018-11-12 15:17:05 -08001689 asyncResp->res.jsonValue["@odata.id"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001690 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries";
Jason M. Bills424c4172019-03-21 13:50:33 -07001691 asyncResp->res.jsonValue["Name"] = "Open BMC Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001692 asyncResp->res.jsonValue["Description"] =
Jason M. Bills424c4172019-03-21 13:50:33 -07001693 "Collection of Crashdump Entries";
Jason M. Billse1f26342018-07-18 12:12:00 -07001694 nlohmann::json &logEntryArray = asyncResp->res.jsonValue["Members"];
1695 logEntryArray = nlohmann::json::array();
Jason M. Billse855dd22019-10-08 11:37:48 -07001696 std::vector<std::string> logIDs;
1697 // Get the list of log entries and build up an empty array big
1698 // enough to hold them
Jason M. Billse1f26342018-07-18 12:12:00 -07001699 for (const std::string &objpath : resp)
1700 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001701 // Get the log ID
Jason M. Billse1f26342018-07-18 12:12:00 -07001702 std::size_t lastPos = objpath.rfind("/");
Jason M. Billse855dd22019-10-08 11:37:48 -07001703 if (lastPos == std::string::npos)
Jason M. Billse1f26342018-07-18 12:12:00 -07001704 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001705 continue;
Jason M. Billse1f26342018-07-18 12:12:00 -07001706 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001707 logIDs.emplace_back(objpath.substr(lastPos + 1));
1708
1709 // Add a space for the log entry to the array
1710 logEntryArray.push_back({});
1711 }
1712 // Now go through and set up async calls to fill in the entries
1713 size_t index = 0;
1714 for (const std::string &logID : logIDs)
1715 {
1716 // Add the log entry to the array
1717 logCrashdumpEntry(asyncResp, logID, logEntryArray[index++]);
Jason M. Billse1f26342018-07-18 12:12:00 -07001718 }
1719 asyncResp->res.jsonValue["Members@odata.count"] =
1720 logEntryArray.size();
1721 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001722 crow::connections::systemBus->async_method_call(
1723 std::move(getLogEntriesCallback),
1724 "xyz.openbmc_project.ObjectMapper",
1725 "/xyz/openbmc_project/object_mapper",
1726 "xyz.openbmc_project.ObjectMapper", "GetSubTreePaths", "", 0,
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001727 std::array<const char *, 1>{crashdumpInterface});
Ed Tanous1da66f72018-07-27 16:13:37 -07001728 }
1729};
1730
Jason M. Bills424c4172019-03-21 13:50:33 -07001731class CrashdumpEntry : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001732{
1733 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001734 CrashdumpEntry(CrowApp &app) :
Jason M. Billsd53dd412019-02-12 17:16:22 -08001735 Node(app,
Jason M. Bills424c4172019-03-21 13:50:33 -07001736 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/",
Ed Tanous1da66f72018-07-27 16:13:37 -07001737 std::string())
1738 {
1739 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001740 {boost::beast::http::verb::get, {{"Login"}}},
1741 {boost::beast::http::verb::head, {{"Login"}}},
1742 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1743 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1744 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1745 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001746 }
1747
1748 private:
1749 void doGet(crow::Response &res, const crow::Request &req,
1750 const std::vector<std::string> &params) override
1751 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001752 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001753 if (params.size() != 1)
1754 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001755 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001756 return;
1757 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001758 const std::string &logID = params[0];
1759 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
1760 }
1761};
1762
1763class CrashdumpFile : public Node
1764{
1765 public:
1766 CrashdumpFile(CrowApp &app) :
1767 Node(app,
1768 "/redfish/v1/Systems/system/LogServices/Crashdump/Entries/<str>/"
1769 "<str>/",
1770 std::string(), std::string())
1771 {
1772 entityPrivileges = {
1773 {boost::beast::http::verb::get, {{"Login"}}},
1774 {boost::beast::http::verb::head, {{"Login"}}},
1775 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1776 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1777 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1778 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1779 }
1780
1781 private:
1782 void doGet(crow::Response &res, const crow::Request &req,
1783 const std::vector<std::string> &params) override
1784 {
1785 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
1786 if (params.size() != 2)
1787 {
1788 messages::internalError(asyncResp->res);
1789 return;
1790 }
1791 const std::string &logID = params[0];
1792 const std::string &fileName = params[1];
1793
1794 auto getStoredLogCallback = [asyncResp, logID, fileName](
Ed Tanousabf2add2019-01-22 16:40:12 -08001795 const boost::system::error_code ec,
1796 const std::variant<std::string> &resp) {
1797 if (ec)
1798 {
1799 BMCWEB_LOG_DEBUG << "failed to get log ec: " << ec.message();
1800 messages::internalError(asyncResp->res);
1801 return;
1802 }
1803 const std::string *log = std::get_if<std::string>(&resp);
1804 if (log == nullptr)
1805 {
1806 messages::internalError(asyncResp->res);
1807 return;
1808 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001809
1810 // Verify the file name parameter is correct
1811 if (fileName != getLogFileName(getLogCreatedTime(*log)))
Ed Tanousabf2add2019-01-22 16:40:12 -08001812 {
Jason M. Billse855dd22019-10-08 11:37:48 -07001813 messages::resourceMissingAtURI(asyncResp->res, fileName);
Ed Tanousabf2add2019-01-22 16:40:12 -08001814 return;
1815 }
Jason M. Billse855dd22019-10-08 11:37:48 -07001816
1817 // Configure this to be a file download when accessed from a browser
1818 asyncResp->res.addHeader("Content-Disposition", "attachment");
1819 asyncResp->res.body() = *log;
Ed Tanousabf2add2019-01-22 16:40:12 -08001820 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001821 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001822 std::move(getStoredLogCallback), crashdumpObject,
1823 crashdumpPath + std::string("/") + logID,
1824 "org.freedesktop.DBus.Properties", "Get", crashdumpInterface,
Jason M. Bills424c4172019-03-21 13:50:33 -07001825 "Log");
Ed Tanous1da66f72018-07-27 16:13:37 -07001826 }
1827};
1828
Jason M. Bills424c4172019-03-21 13:50:33 -07001829class OnDemandCrashdump : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001830{
1831 public:
Jason M. Bills424c4172019-03-21 13:50:33 -07001832 OnDemandCrashdump(CrowApp &app) :
1833 Node(app,
1834 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1835 "Crashdump.OnDemand/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001836 {
1837 entityPrivileges = {
Jason M. Billse1f26342018-07-18 12:12:00 -07001838 {boost::beast::http::verb::get, {{"Login"}}},
1839 {boost::beast::http::verb::head, {{"Login"}}},
1840 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1841 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1842 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1843 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
Ed Tanous1da66f72018-07-27 16:13:37 -07001844 }
1845
1846 private:
1847 void doPost(crow::Response &res, const crow::Request &req,
1848 const std::vector<std::string> &params) override
1849 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001850 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001851
Jason M. Bills48e46392019-02-13 12:58:37 -08001852 auto generateonDemandLogCallback =
Jason M. Billse1f26342018-07-18 12:12:00 -07001853 [asyncResp](const boost::system::error_code ec,
1854 const std::string &resp) {
Ed Tanous1da66f72018-07-27 16:13:37 -07001855 if (ec)
1856 {
1857 if (ec.value() ==
1858 boost::system::errc::operation_not_supported)
1859 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001860 messages::resourceInStandby(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001861 }
Jason M. Bills4363d3b2019-11-26 15:04:29 -08001862 else if (ec.value() ==
1863 boost::system::errc::device_or_resource_busy)
1864 {
1865 messages::serviceTemporarilyUnavailable(asyncResp->res,
1866 "60");
1867 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001868 else
1869 {
Jason M. Billsf12894f2018-10-09 12:45:45 -07001870 messages::internalError(asyncResp->res);
Ed Tanous1da66f72018-07-27 16:13:37 -07001871 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001872 return;
1873 }
Jason M. Bills3e0414f2019-10-29 12:46:33 -07001874 asyncResp->res.result(boost::beast::http::status::no_content);
Ed Tanous1da66f72018-07-27 16:13:37 -07001875 };
1876 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001877 std::move(generateonDemandLogCallback), crashdumpObject,
1878 crashdumpPath, crashdumpOnDemandInterface, "GenerateOnDemandLog");
Ed Tanous1da66f72018-07-27 16:13:37 -07001879 }
1880};
1881
Jason M. Billse1f26342018-07-18 12:12:00 -07001882class SendRawPECI : public Node
Ed Tanous1da66f72018-07-27 16:13:37 -07001883{
1884 public:
Jason M. Billse1f26342018-07-18 12:12:00 -07001885 SendRawPECI(CrowApp &app) :
Jason M. Bills424c4172019-03-21 13:50:33 -07001886 Node(app,
1887 "/redfish/v1/Systems/system/LogServices/Crashdump/Actions/Oem/"
1888 "Crashdump.SendRawPeci/")
Ed Tanous1da66f72018-07-27 16:13:37 -07001889 {
1890 entityPrivileges = {
1891 {boost::beast::http::verb::get, {{"ConfigureComponents"}}},
1892 {boost::beast::http::verb::head, {{"ConfigureComponents"}}},
1893 {boost::beast::http::verb::patch, {{"ConfigureComponents"}}},
1894 {boost::beast::http::verb::put, {{"ConfigureComponents"}}},
1895 {boost::beast::http::verb::delete_, {{"ConfigureComponents"}}},
1896 {boost::beast::http::verb::post, {{"ConfigureComponents"}}}};
1897 }
1898
1899 private:
1900 void doPost(crow::Response &res, const crow::Request &req,
1901 const std::vector<std::string> &params) override
1902 {
Jason M. Billse1f26342018-07-18 12:12:00 -07001903 std::shared_ptr<AsyncResp> asyncResp = std::make_shared<AsyncResp>(res);
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001904 std::vector<std::vector<uint8_t>> peciCommands;
Ed Tanousb1556422018-10-16 14:09:17 -07001905
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001906 nlohmann::json reqJson =
1907 nlohmann::json::parse(req.body, nullptr, false);
1908 if (reqJson.find("PECICommands") != reqJson.end())
1909 {
1910 if (!json_util::readJson(req, res, "PECICommands", peciCommands))
1911 {
1912 return;
1913 }
1914 uint32_t idx = 0;
1915 for (auto const &cmd : peciCommands)
1916 {
1917 if (cmd.size() < 3)
1918 {
1919 std::string s("[");
1920 for (auto const &val : cmd)
1921 {
1922 if (val != *cmd.begin())
1923 {
1924 s += ",";
1925 }
1926 s += std::to_string(val);
1927 }
1928 s += "]";
1929 messages::actionParameterValueFormatError(
1930 res, s, "PECICommands[" + std::to_string(idx) + "]",
1931 "SendRawPeci");
1932 return;
1933 }
1934 idx++;
1935 }
1936 }
1937 else
1938 {
1939 /* This interface is deprecated */
1940 uint8_t clientAddress = 0;
1941 uint8_t readLength = 0;
1942 std::vector<uint8_t> peciCommand;
1943 if (!json_util::readJson(req, res, "ClientAddress", clientAddress,
1944 "ReadLength", readLength, "PECICommand",
1945 peciCommand))
1946 {
1947 return;
1948 }
1949 peciCommands.push_back({clientAddress, 0, readLength});
1950 peciCommands[0].insert(peciCommands[0].end(), peciCommand.begin(),
1951 peciCommand.end());
1952 }
Ed Tanous1da66f72018-07-27 16:13:37 -07001953 // Callback to return the Raw PECI response
Jason M. Billse1f26342018-07-18 12:12:00 -07001954 auto sendRawPECICallback =
1955 [asyncResp](const boost::system::error_code ec,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001956 const std::vector<std::vector<uint8_t>> &resp) {
Jason M. Billse1f26342018-07-18 12:12:00 -07001957 if (ec)
1958 {
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001959 BMCWEB_LOG_DEBUG << "failed to process PECI commands ec: "
Jason M. Billse1f26342018-07-18 12:12:00 -07001960 << ec.message();
Jason M. Billsf12894f2018-10-09 12:45:45 -07001961 messages::internalError(asyncResp->res);
Jason M. Billse1f26342018-07-18 12:12:00 -07001962 return;
1963 }
1964 asyncResp->res.jsonValue = {{"Name", "PECI Command Response"},
1965 {"PECIResponse", resp}};
1966 };
Ed Tanous1da66f72018-07-27 16:13:37 -07001967 // Call the SendRawPECI command with the provided data
1968 crow::connections::systemBus->async_method_call(
Jason M. Bills5b61b5e2019-10-16 10:59:02 -07001969 std::move(sendRawPECICallback), crashdumpObject, crashdumpPath,
Karthick Sundarrajan8724c292020-01-06 09:04:48 -08001970 crashdumpRawPECIInterface, "SendRawPeci", peciCommands);
Ed Tanous1da66f72018-07-27 16:13:37 -07001971 }
1972};
1973
Andrew Geisslercb92c032018-08-17 07:56:14 -07001974/**
1975 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
1976 */
1977class DBusLogServiceActionsClear : public Node
1978{
1979 public:
1980 DBusLogServiceActionsClear(CrowApp &app) :
1981 Node(app, "/redfish/v1/Systems/system/LogServices/EventLog/Actions/"
Tim Lee1f56a3a2019-10-09 10:17:57 +08001982 "LogService.ClearLog")
Andrew Geisslercb92c032018-08-17 07:56:14 -07001983 {
1984 entityPrivileges = {
1985 {boost::beast::http::verb::get, {{"Login"}}},
1986 {boost::beast::http::verb::head, {{"Login"}}},
1987 {boost::beast::http::verb::patch, {{"ConfigureManager"}}},
1988 {boost::beast::http::verb::put, {{"ConfigureManager"}}},
1989 {boost::beast::http::verb::delete_, {{"ConfigureManager"}}},
1990 {boost::beast::http::verb::post, {{"ConfigureManager"}}}};
1991 }
1992
1993 private:
1994 /**
1995 * Function handles POST method request.
1996 * The Clear Log actions does not require any parameter.The action deletes
1997 * all entries found in the Entries collection for this Log Service.
1998 */
1999 void doPost(crow::Response &res, const crow::Request &req,
2000 const std::vector<std::string> &params) override
2001 {
2002 BMCWEB_LOG_DEBUG << "Do delete all entries.";
2003
2004 auto asyncResp = std::make_shared<AsyncResp>(res);
2005 // Process response from Logging service.
2006 auto resp_handler = [asyncResp](const boost::system::error_code ec) {
2007 BMCWEB_LOG_DEBUG << "doClearLog resp_handler callback: Done";
2008 if (ec)
2009 {
2010 // TODO Handle for specific error code
2011 BMCWEB_LOG_ERROR << "doClearLog resp_handler got error " << ec;
2012 asyncResp->res.result(
2013 boost::beast::http::status::internal_server_error);
2014 return;
2015 }
2016
2017 asyncResp->res.result(boost::beast::http::status::no_content);
2018 };
2019
2020 // Make call to Logging service to request Clear Log
2021 crow::connections::systemBus->async_method_call(
2022 resp_handler, "xyz.openbmc_project.Logging",
2023 "/xyz/openbmc_project/logging",
2024 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
2025 }
2026};
Ed Tanous1da66f72018-07-27 16:13:37 -07002027} // namespace redfish