1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5
6 #include "bmcweb_config.h"
7
8 #include "app.hpp"
9 #include "async_resp.hpp"
10 #include "dbus_singleton.hpp"
11 #include "dbus_utility.hpp"
12 #include "error_messages.hpp"
13 #include "generated/enums/log_entry.hpp"
14 #include "generated/enums/log_service.hpp"
15 #include "http_body.hpp"
16 #include "http_request.hpp"
17 #include "http_response.hpp"
18 #include "http_utility.hpp"
19 #include "human_sort.hpp"
20 #include "logging.hpp"
21 #include "query.hpp"
22 #include "registries.hpp"
23 #include "registries/privilege_registry.hpp"
24 #include "str_utility.hpp"
25 #include "task.hpp"
26 #include "task_messages.hpp"
27 #include "utils/dbus_event_log_entry.hpp"
28 #include "utils/dbus_utils.hpp"
29 #include "utils/json_utils.hpp"
30 #include "utils/query_param.hpp"
31 #include "utils/time_utils.hpp"
32
33 #include <asm-generic/errno.h>
34 #include <systemd/sd-bus.h>
35 #include <tinyxml2.h>
36 #include <unistd.h>
37
38 #include <boost/beast/http/field.hpp>
39 #include <boost/beast/http/status.hpp>
40 #include <boost/beast/http/verb.hpp>
41 #include <boost/system/linux_error.hpp>
42 #include <boost/url/format.hpp>
43 #include <boost/url/url.hpp>
44 #include <sdbusplus/message.hpp>
45 #include <sdbusplus/message/native_types.hpp>
46 #include <sdbusplus/unpack_properties.hpp>
47
48 #include <algorithm>
49 #include <array>
50 #include <chrono>
51 #include <cstddef>
52 #include <cstdint>
53 #include <cstdio>
54 #include <ctime>
55 #include <filesystem>
56 #include <format>
57 #include <fstream>
58 #include <functional>
59 #include <iomanip>
60 #include <iterator>
61 #include <memory>
62 #include <optional>
63 #include <ranges>
64 #include <span>
65 #include <sstream>
66 #include <string>
67 #include <string_view>
68 #include <system_error>
69 #include <utility>
70 #include <variant>
71 #include <vector>
72
73 namespace redfish
74 {
75
76 constexpr const char* crashdumpObject = "com.intel.crashdump";
77 constexpr const char* crashdumpPath = "/com/intel/crashdump";
78 constexpr const char* crashdumpInterface = "com.intel.crashdump";
79 constexpr const char* deleteAllInterface =
80 "xyz.openbmc_project.Collection.DeleteAll";
81 constexpr const char* crashdumpOnDemandInterface =
82 "com.intel.crashdump.OnDemand";
83 constexpr const char* crashdumpTelemetryInterface =
84 "com.intel.crashdump.Telemetry";
85
86 enum class DumpCreationProgress
87 {
88 DUMP_CREATE_SUCCESS,
89 DUMP_CREATE_FAILED,
90 DUMP_CREATE_INPROGRESS
91 };
92
translateSeverityDbusToRedfish(const std::string & s)93 inline std::string translateSeverityDbusToRedfish(const std::string& s)
94 {
95 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Alert") ||
96 (s == "xyz.openbmc_project.Logging.Entry.Level.Critical") ||
97 (s == "xyz.openbmc_project.Logging.Entry.Level.Emergency") ||
98 (s == "xyz.openbmc_project.Logging.Entry.Level.Error"))
99 {
100 return "Critical";
101 }
102 if ((s == "xyz.openbmc_project.Logging.Entry.Level.Debug") ||
103 (s == "xyz.openbmc_project.Logging.Entry.Level.Informational") ||
104 (s == "xyz.openbmc_project.Logging.Entry.Level.Notice"))
105 {
106 return "OK";
107 }
108 if (s == "xyz.openbmc_project.Logging.Entry.Level.Warning")
109 {
110 return "Warning";
111 }
112 return "";
113 }
114
getProviderNotifyAction(const std::string & notify)115 inline std::optional<bool> getProviderNotifyAction(const std::string& notify)
116 {
117 std::optional<bool> notifyAction;
118 if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Notify")
119 {
120 notifyAction = true;
121 }
122 else if (notify == "xyz.openbmc_project.Logging.Entry.Notify.Inhibit")
123 {
124 notifyAction = false;
125 }
126
127 return notifyAction;
128 }
129
getDumpPath(std::string_view dumpType)130 inline std::string getDumpPath(std::string_view dumpType)
131 {
132 std::string dbusDumpPath = "/xyz/openbmc_project/dump/";
133 std::ranges::transform(dumpType, std::back_inserter(dbusDumpPath),
134 bmcweb::asciiToLower);
135
136 return dbusDumpPath;
137 }
138
getUniqueEntryID(const std::string & logEntry,std::string & entryID,const bool firstEntry=true)139 inline bool getUniqueEntryID(const std::string& logEntry, std::string& entryID,
140 const bool firstEntry = true)
141 {
142 static time_t prevTs = 0;
143 static int index = 0;
144 if (firstEntry)
145 {
146 prevTs = 0;
147 }
148
149 // Get the entry timestamp
150 std::time_t curTs = 0;
151 std::tm timeStruct = {};
152 std::istringstream entryStream(logEntry);
153 if (entryStream >> std::get_time(&timeStruct, "%Y-%m-%dT%H:%M:%S"))
154 {
155 curTs = std::mktime(&timeStruct);
156 }
157 // If the timestamp isn't unique, increment the index
158 if (curTs == prevTs)
159 {
160 index++;
161 }
162 else
163 {
164 // Otherwise, reset it
165 index = 0;
166 }
167 // Save the timestamp
168 prevTs = curTs;
169
170 entryID = std::to_string(curTs);
171 if (index > 0)
172 {
173 entryID += "_" + std::to_string(index);
174 }
175 return true;
176 }
177
getRedfishLogFiles(std::vector<std::filesystem::path> & redfishLogFiles)178 inline bool getRedfishLogFiles(
179 std::vector<std::filesystem::path>& redfishLogFiles)
180 {
181 static const std::filesystem::path redfishLogDir = "/var/log";
182 static const std::string redfishLogFilename = "redfish";
183
184 // Loop through the directory looking for redfish log files
185 for (const std::filesystem::directory_entry& dirEnt :
186 std::filesystem::directory_iterator(redfishLogDir))
187 {
188 // If we find a redfish log file, save the path
189 std::string filename = dirEnt.path().filename();
190 if (filename.starts_with(redfishLogFilename))
191 {
192 redfishLogFiles.emplace_back(redfishLogDir / filename);
193 }
194 }
195 // As the log files rotate, they are appended with a ".#" that is higher for
196 // the older logs. Since we don't expect more than 10 log files, we
197 // can just sort the list to get them in order from newest to oldest
198 std::ranges::sort(redfishLogFiles);
199
200 return !redfishLogFiles.empty();
201 }
202
mapDbusOriginatorTypeToRedfish(const std::string & originatorType)203 inline log_entry::OriginatorTypes mapDbusOriginatorTypeToRedfish(
204 const std::string& originatorType)
205 {
206 if (originatorType ==
207 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client")
208 {
209 return log_entry::OriginatorTypes::Client;
210 }
211 if (originatorType ==
212 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Internal")
213 {
214 return log_entry::OriginatorTypes::Internal;
215 }
216 if (originatorType ==
217 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.SupportingService")
218 {
219 return log_entry::OriginatorTypes::SupportingService;
220 }
221 return log_entry::OriginatorTypes::Invalid;
222 }
223
parseDumpEntryFromDbusObject(const dbus::utility::ManagedObjectType::value_type & object,std::string & dumpStatus,uint64_t & size,uint64_t & timestampUs,std::string & originatorId,log_entry::OriginatorTypes & originatorType,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)224 inline void parseDumpEntryFromDbusObject(
225 const dbus::utility::ManagedObjectType::value_type& object,
226 std::string& dumpStatus, uint64_t& size, uint64_t& timestampUs,
227 std::string& originatorId, log_entry::OriginatorTypes& originatorType,
228 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
229 {
230 for (const auto& interfaceMap : object.second)
231 {
232 if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
233 {
234 for (const auto& propertyMap : interfaceMap.second)
235 {
236 if (propertyMap.first == "Status")
237 {
238 const auto* status =
239 std::get_if<std::string>(&propertyMap.second);
240 if (status == nullptr)
241 {
242 messages::internalError(asyncResp->res);
243 break;
244 }
245 dumpStatus = *status;
246 }
247 }
248 }
249 else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
250 {
251 for (const auto& propertyMap : interfaceMap.second)
252 {
253 if (propertyMap.first == "Size")
254 {
255 const auto* sizePtr =
256 std::get_if<uint64_t>(&propertyMap.second);
257 if (sizePtr == nullptr)
258 {
259 messages::internalError(asyncResp->res);
260 break;
261 }
262 size = *sizePtr;
263 break;
264 }
265 }
266 }
267 else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime")
268 {
269 for (const auto& propertyMap : interfaceMap.second)
270 {
271 if (propertyMap.first == "Elapsed")
272 {
273 const uint64_t* usecsTimeStamp =
274 std::get_if<uint64_t>(&propertyMap.second);
275 if (usecsTimeStamp == nullptr)
276 {
277 messages::internalError(asyncResp->res);
278 break;
279 }
280 timestampUs = *usecsTimeStamp;
281 break;
282 }
283 }
284 }
285 else if (interfaceMap.first ==
286 "xyz.openbmc_project.Common.OriginatedBy")
287 {
288 for (const auto& propertyMap : interfaceMap.second)
289 {
290 if (propertyMap.first == "OriginatorId")
291 {
292 const std::string* id =
293 std::get_if<std::string>(&propertyMap.second);
294 if (id == nullptr)
295 {
296 messages::internalError(asyncResp->res);
297 break;
298 }
299 originatorId = *id;
300 }
301
302 if (propertyMap.first == "OriginatorType")
303 {
304 const std::string* type =
305 std::get_if<std::string>(&propertyMap.second);
306 if (type == nullptr)
307 {
308 messages::internalError(asyncResp->res);
309 break;
310 }
311
312 originatorType = mapDbusOriginatorTypeToRedfish(*type);
313 if (originatorType == log_entry::OriginatorTypes::Invalid)
314 {
315 messages::internalError(asyncResp->res);
316 break;
317 }
318 }
319 }
320 }
321 }
322 }
323
getDumpEntriesPath(const std::string & dumpType)324 static std::string getDumpEntriesPath(const std::string& dumpType)
325 {
326 std::string entriesPath;
327
328 if (dumpType == "BMC")
329 {
330 entriesPath =
331 std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/",
332 BMCWEB_REDFISH_MANAGER_URI_NAME);
333 }
334 else if (dumpType == "FaultLog")
335 {
336 entriesPath =
337 std::format("/redfish/v1/Managers/{}/LogServices/FaultLog/Entries/",
338 BMCWEB_REDFISH_MANAGER_URI_NAME);
339 }
340 else if (dumpType == "System")
341 {
342 entriesPath =
343 std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/",
344 BMCWEB_REDFISH_SYSTEM_URI_NAME);
345 }
346 else
347 {
348 BMCWEB_LOG_ERROR("getDumpEntriesPath() invalid dump type: {}",
349 dumpType);
350 }
351
352 // Returns empty string on error
353 return entriesPath;
354 }
355
getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)356 inline void getDumpEntryCollection(
357 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
358 const std::string& dumpType)
359 {
360 std::string entriesPath = getDumpEntriesPath(dumpType);
361 if (entriesPath.empty())
362 {
363 messages::internalError(asyncResp->res);
364 return;
365 }
366
367 sdbusplus::message::object_path path("/xyz/openbmc_project/dump");
368 dbus::utility::getManagedObjects(
369 "xyz.openbmc_project.Dump.Manager", path,
370 [asyncResp, entriesPath,
371 dumpType](const boost::system::error_code& ec,
372 const dbus::utility::ManagedObjectType& objects) {
373 if (ec)
374 {
375 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec);
376 messages::internalError(asyncResp->res);
377 return;
378 }
379
380 // Remove ending slash
381 std::string odataIdStr = entriesPath;
382 if (!odataIdStr.empty())
383 {
384 odataIdStr.pop_back();
385 }
386
387 asyncResp->res.jsonValue["@odata.type"] =
388 "#LogEntryCollection.LogEntryCollection";
389 asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
390 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
391 asyncResp->res.jsonValue["Description"] =
392 "Collection of " + dumpType + " Dump Entries";
393
394 nlohmann::json::array_t entriesArray;
395 std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/";
396
397 dbus::utility::ManagedObjectType resp(objects);
398 std::ranges::sort(resp, [](const auto& l, const auto& r) {
399 return AlphanumLess<std::string>()(l.first.filename(),
400 r.first.filename());
401 });
402
403 for (auto& object : resp)
404 {
405 if (object.first.str.find(dumpEntryPath) == std::string::npos)
406 {
407 continue;
408 }
409 uint64_t timestampUs = 0;
410 uint64_t size = 0;
411 std::string dumpStatus;
412 std::string originatorId;
413 log_entry::OriginatorTypes originatorType =
414 log_entry::OriginatorTypes::Internal;
415 nlohmann::json::object_t thisEntry;
416
417 std::string entryID = object.first.filename();
418 if (entryID.empty())
419 {
420 continue;
421 }
422
423 parseDumpEntryFromDbusObject(object, dumpStatus, size,
424 timestampUs, originatorId,
425 originatorType, asyncResp);
426
427 if (dumpStatus !=
428 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
429 !dumpStatus.empty())
430 {
431 // Dump status is not Complete, no need to enumerate
432 continue;
433 }
434
435 thisEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry";
436 thisEntry["@odata.id"] = entriesPath + entryID;
437 thisEntry["Id"] = entryID;
438 thisEntry["EntryType"] = "Event";
439 thisEntry["Name"] = dumpType + " Dump Entry";
440 thisEntry["Created"] =
441 redfish::time_utils::getDateTimeUintUs(timestampUs);
442
443 if (!originatorId.empty())
444 {
445 thisEntry["Originator"] = originatorId;
446 thisEntry["OriginatorType"] = originatorType;
447 }
448
449 if (dumpType == "BMC")
450 {
451 thisEntry["DiagnosticDataType"] = "Manager";
452 thisEntry["AdditionalDataURI"] =
453 entriesPath + entryID + "/attachment";
454 thisEntry["AdditionalDataSizeBytes"] = size;
455 }
456 else if (dumpType == "System")
457 {
458 thisEntry["DiagnosticDataType"] = "OEM";
459 thisEntry["OEMDiagnosticDataType"] = "System";
460 thisEntry["AdditionalDataURI"] =
461 entriesPath + entryID + "/attachment";
462 thisEntry["AdditionalDataSizeBytes"] = size;
463 }
464 entriesArray.emplace_back(std::move(thisEntry));
465 }
466 asyncResp->res.jsonValue["Members@odata.count"] =
467 entriesArray.size();
468 asyncResp->res.jsonValue["Members"] = std::move(entriesArray);
469 });
470 }
471
getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & dumpType)472 inline void getDumpEntryById(
473 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
474 const std::string& entryID, const std::string& dumpType)
475 {
476 std::string entriesPath = getDumpEntriesPath(dumpType);
477 if (entriesPath.empty())
478 {
479 messages::internalError(asyncResp->res);
480 return;
481 }
482
483 sdbusplus::message::object_path path("/xyz/openbmc_project/dump");
484 dbus::utility::getManagedObjects(
485 "xyz.openbmc_project.Dump.Manager", path,
486 [asyncResp, entryID, dumpType,
487 entriesPath](const boost::system::error_code& ec,
488 const dbus::utility::ManagedObjectType& resp) {
489 if (ec)
490 {
491 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec);
492 messages::internalError(asyncResp->res);
493 return;
494 }
495
496 bool foundDumpEntry = false;
497 std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/";
498
499 for (const auto& objectPath : resp)
500 {
501 if (objectPath.first.str != dumpEntryPath + entryID)
502 {
503 continue;
504 }
505
506 foundDumpEntry = true;
507 uint64_t timestampUs = 0;
508 uint64_t size = 0;
509 std::string dumpStatus;
510 std::string originatorId;
511 log_entry::OriginatorTypes originatorType =
512 log_entry::OriginatorTypes::Internal;
513
514 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size,
515 timestampUs, originatorId,
516 originatorType, asyncResp);
517
518 if (dumpStatus !=
519 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
520 !dumpStatus.empty())
521 {
522 // Dump status is not Complete
523 // return not found until status is changed to Completed
524 messages::resourceNotFound(asyncResp->res,
525 dumpType + " dump", entryID);
526 return;
527 }
528
529 asyncResp->res.jsonValue["@odata.type"] =
530 "#LogEntry.v1_11_0.LogEntry";
531 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
532 asyncResp->res.jsonValue["Id"] = entryID;
533 asyncResp->res.jsonValue["EntryType"] = "Event";
534 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
535 asyncResp->res.jsonValue["Created"] =
536 redfish::time_utils::getDateTimeUintUs(timestampUs);
537
538 if (!originatorId.empty())
539 {
540 asyncResp->res.jsonValue["Originator"] = originatorId;
541 asyncResp->res.jsonValue["OriginatorType"] = originatorType;
542 }
543
544 if (dumpType == "BMC")
545 {
546 asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
547 asyncResp->res.jsonValue["AdditionalDataURI"] =
548 entriesPath + entryID + "/attachment";
549 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
550 }
551 else if (dumpType == "System")
552 {
553 asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
554 asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
555 "System";
556 asyncResp->res.jsonValue["AdditionalDataURI"] =
557 entriesPath + entryID + "/attachment";
558 asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
559 }
560 }
561 if (!foundDumpEntry)
562 {
563 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID);
564 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
565 entryID);
566 return;
567 }
568 });
569 }
570
deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & dumpType)571 inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
572 const std::string& entryID,
573 const std::string& dumpType)
574 {
575 auto respHandler = [asyncResp,
576 entryID](const boost::system::error_code& ec) {
577 BMCWEB_LOG_DEBUG("Dump Entry doDelete callback: Done");
578 if (ec)
579 {
580 if (ec.value() == EBADR)
581 {
582 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
583 return;
584 }
585 BMCWEB_LOG_ERROR(
586 "Dump (DBus) doDelete respHandler got error {} entryID={}", ec,
587 entryID);
588 messages::internalError(asyncResp->res);
589 return;
590 }
591 };
592
593 dbus::utility::async_method_call(
594 asyncResp, respHandler, "xyz.openbmc_project.Dump.Manager",
595 std::format("{}/entry/{}", getDumpPath(dumpType), entryID),
596 "xyz.openbmc_project.Object.Delete", "Delete");
597 }
checkSizeLimit(int fd,crow::Response & res)598 inline bool checkSizeLimit(int fd, crow::Response& res)
599 {
600 long long int size = lseek(fd, 0, SEEK_END);
601 if (size <= 0)
602 {
603 BMCWEB_LOG_ERROR("Failed to get size of file, lseek() returned {}",
604 size);
605 messages::internalError(res);
606 return false;
607 }
608
609 // Arbitrary max size of 20MB to accommodate BMC dumps
610 constexpr long long int maxFileSize = 20LL * 1024LL * 1024LL;
611 if (size > maxFileSize)
612 {
613 BMCWEB_LOG_ERROR("File size {} exceeds maximum allowed size of {}",
614 size, maxFileSize);
615 messages::internalError(res);
616 return false;
617 }
618 off_t rc = lseek(fd, 0, SEEK_SET);
619 if (rc < 0)
620 {
621 BMCWEB_LOG_ERROR("Failed to reset file offset to 0");
622 messages::internalError(res);
623 return false;
624 }
625 return true;
626 }
downloadEntryCallback(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & downloadEntryType,const boost::system::error_code & ec,const sdbusplus::message::unix_fd & unixfd)627 inline void downloadEntryCallback(
628 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
629 const std::string& entryID, const std::string& downloadEntryType,
630 const boost::system::error_code& ec,
631 const sdbusplus::message::unix_fd& unixfd)
632 {
633 if (ec.value() == EBADR)
634 {
635 messages::resourceNotFound(asyncResp->res, "EntryAttachment", entryID);
636 return;
637 }
638 if (ec)
639 {
640 BMCWEB_LOG_ERROR("DBUS response error: {}", ec);
641 messages::internalError(asyncResp->res);
642 return;
643 }
644
645 // Make sure we know how to process the retrieved entry attachment
646 if ((downloadEntryType != "BMC") && (downloadEntryType != "System"))
647 {
648 BMCWEB_LOG_ERROR("downloadEntryCallback() invalid entry type: {}",
649 downloadEntryType);
650 messages::internalError(asyncResp->res);
651 }
652
653 int fd = -1;
654 fd = dup(unixfd);
655 if (fd < 0)
656 {
657 BMCWEB_LOG_ERROR("Failed to open file");
658 messages::internalError(asyncResp->res);
659 return;
660 }
661 if (!checkSizeLimit(fd, asyncResp->res))
662 {
663 close(fd);
664 return;
665 }
666 if (downloadEntryType == "System")
667 {
668 if (!asyncResp->res.openFd(fd, bmcweb::EncodingType::Base64))
669 {
670 messages::internalError(asyncResp->res);
671 close(fd);
672 return;
673 }
674 asyncResp->res.addHeader(
675 boost::beast::http::field::content_transfer_encoding, "Base64");
676 return;
677 }
678 if (!asyncResp->res.openFd(fd))
679 {
680 messages::internalError(asyncResp->res);
681 close(fd);
682 return;
683 }
684 asyncResp->res.addHeader(boost::beast::http::field::content_type,
685 "application/octet-stream");
686 }
687
downloadDumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & dumpType)688 inline void downloadDumpEntry(
689 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
690 const std::string& entryID, const std::string& dumpType)
691 {
692 if (dumpType != "BMC")
693 {
694 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID);
695 messages::resourceNotFound(asyncResp->res, dumpType + " dump", entryID);
696 return;
697 }
698
699 std::string dumpEntryPath =
700 std::format("{}/entry/{}", getDumpPath(dumpType), entryID);
701
702 auto downloadDumpEntryHandler =
703 [asyncResp, entryID,
704 dumpType](const boost::system::error_code& ec,
705 const sdbusplus::message::unix_fd& unixfd) {
706 downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd);
707 };
708
709 dbus::utility::async_method_call(
710 asyncResp, std::move(downloadDumpEntryHandler),
711 "xyz.openbmc_project.Dump.Manager", dumpEntryPath,
712 "xyz.openbmc_project.Dump.Entry", "GetFileHandle");
713 }
714
downloadEventLogEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & entryID,const std::string & dumpType)715 inline void downloadEventLogEntry(
716 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
717 const std::string& systemName, const std::string& entryID,
718 const std::string& dumpType)
719 {
720 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
721 {
722 // Option currently returns no systems. TBD
723 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
724 systemName);
725 return;
726 }
727 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
728 {
729 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
730 systemName);
731 return;
732 }
733
734 std::string entryPath =
735 sdbusplus::message::object_path("/xyz/openbmc_project/logging/entry") /
736 entryID;
737
738 auto downloadEventLogEntryHandler =
739 [asyncResp, entryID,
740 dumpType](const boost::system::error_code& ec,
741 const sdbusplus::message::unix_fd& unixfd) {
742 downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd);
743 };
744
745 dbus::utility::async_method_call(
746 asyncResp, std::move(downloadEventLogEntryHandler),
747 "xyz.openbmc_project.Logging", entryPath,
748 "xyz.openbmc_project.Logging.Entry", "GetEntry");
749 }
750
mapDbusStatusToDumpProgress(const std::string & status)751 inline DumpCreationProgress mapDbusStatusToDumpProgress(
752 const std::string& status)
753 {
754 if (status ==
755 "xyz.openbmc_project.Common.Progress.OperationStatus.Failed" ||
756 status == "xyz.openbmc_project.Common.Progress.OperationStatus.Aborted")
757 {
758 return DumpCreationProgress::DUMP_CREATE_FAILED;
759 }
760 if (status ==
761 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")
762 {
763 return DumpCreationProgress::DUMP_CREATE_SUCCESS;
764 }
765 return DumpCreationProgress::DUMP_CREATE_INPROGRESS;
766 }
767
getDumpCompletionStatus(const dbus::utility::DBusPropertiesMap & values)768 inline DumpCreationProgress getDumpCompletionStatus(
769 const dbus::utility::DBusPropertiesMap& values)
770 {
771 for (const auto& [key, val] : values)
772 {
773 if (key == "Status")
774 {
775 const std::string* value = std::get_if<std::string>(&val);
776 if (value == nullptr)
777 {
778 BMCWEB_LOG_ERROR("Status property value is null");
779 return DumpCreationProgress::DUMP_CREATE_FAILED;
780 }
781 return mapDbusStatusToDumpProgress(*value);
782 }
783 }
784 return DumpCreationProgress::DUMP_CREATE_INPROGRESS;
785 }
786
getDumpEntryPath(const std::string & dumpPath)787 inline std::string getDumpEntryPath(const std::string& dumpPath)
788 {
789 if (dumpPath == "/xyz/openbmc_project/dump/bmc/entry")
790 {
791 return std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/",
792 BMCWEB_REDFISH_MANAGER_URI_NAME);
793 }
794 if (dumpPath == "/xyz/openbmc_project/dump/system/entry")
795 {
796 return std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/",
797 BMCWEB_REDFISH_SYSTEM_URI_NAME);
798 }
799 return "";
800 }
801
createDumpTaskCallback(task::Payload && payload,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const sdbusplus::message::object_path & createdObjPath)802 inline void createDumpTaskCallback(
803 task::Payload&& payload,
804 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
805 const sdbusplus::message::object_path& createdObjPath)
806 {
807 const std::string dumpPath = createdObjPath.parent_path().str;
808 const std::string dumpId = createdObjPath.filename();
809
810 std::string dumpEntryPath = getDumpEntryPath(dumpPath);
811
812 if (dumpEntryPath.empty())
813 {
814 BMCWEB_LOG_ERROR("Invalid dump type received");
815 messages::internalError(asyncResp->res);
816 return;
817 }
818
819 dbus::utility::async_method_call(
820 asyncResp,
821 [asyncResp, payload = std::move(payload), createdObjPath,
822 dumpEntryPath{std::move(dumpEntryPath)},
823 dumpId](const boost::system::error_code& ec,
824 const std::string& introspectXml) {
825 if (ec)
826 {
827 BMCWEB_LOG_ERROR("Introspect call failed with error: {}",
828 ec.message());
829 messages::internalError(asyncResp->res);
830 return;
831 }
832
833 // Check if the created dump object has implemented Progress
834 // interface to track dump completion. If yes, fetch the "Status"
835 // property of the interface, modify the task state accordingly.
836 // Else, return task completed.
837 tinyxml2::XMLDocument doc;
838
839 doc.Parse(introspectXml.data(), introspectXml.size());
840 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
841 if (pRoot == nullptr)
842 {
843 BMCWEB_LOG_ERROR("XML document failed to parse");
844 messages::internalError(asyncResp->res);
845 return;
846 }
847 tinyxml2::XMLElement* interfaceNode =
848 pRoot->FirstChildElement("interface");
849
850 bool isProgressIntfPresent = false;
851 while (interfaceNode != nullptr)
852 {
853 const char* thisInterfaceName =
854 interfaceNode->Attribute("name");
855 if (thisInterfaceName != nullptr)
856 {
857 if (thisInterfaceName ==
858 std::string_view("xyz.openbmc_project.Common.Progress"))
859 {
860 interfaceNode =
861 interfaceNode->NextSiblingElement("interface");
862 continue;
863 }
864 isProgressIntfPresent = true;
865 break;
866 }
867 interfaceNode = interfaceNode->NextSiblingElement("interface");
868 }
869
870 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
871 [createdObjPath, dumpEntryPath, dumpId, isProgressIntfPresent](
872 const boost::system::error_code& ec2,
873 sdbusplus::message_t& msg,
874 const std::shared_ptr<task::TaskData>& taskData) {
875 if (ec2)
876 {
877 BMCWEB_LOG_ERROR("{}: Error in creating dump",
878 createdObjPath.str);
879 taskData->messages.emplace_back(
880 messages::internalError());
881 taskData->state = "Cancelled";
882 return task::completed;
883 }
884
885 if (isProgressIntfPresent)
886 {
887 dbus::utility::DBusPropertiesMap values;
888 std::string prop;
889 msg.read(prop, values);
890
891 DumpCreationProgress dumpStatus =
892 getDumpCompletionStatus(values);
893 if (dumpStatus ==
894 DumpCreationProgress::DUMP_CREATE_FAILED)
895 {
896 BMCWEB_LOG_ERROR("{}: Error in creating dump",
897 createdObjPath.str);
898 taskData->state = "Cancelled";
899 return task::completed;
900 }
901
902 if (dumpStatus ==
903 DumpCreationProgress::DUMP_CREATE_INPROGRESS)
904 {
905 BMCWEB_LOG_DEBUG(
906 "{}: Dump creation task is in progress",
907 createdObjPath.str);
908 return !task::completed;
909 }
910 }
911
912 nlohmann::json retMessage = messages::success();
913 taskData->messages.emplace_back(retMessage);
914
915 boost::urls::url url = boost::urls::format(
916 "/redfish/v1/Managers/{}/LogServices/Dump/Entries/{}",
917 BMCWEB_REDFISH_MANAGER_URI_NAME, dumpId);
918
919 std::string headerLoc = "Location: ";
920 headerLoc += url.buffer();
921
922 taskData->payload->httpHeaders.emplace_back(
923 std::move(headerLoc));
924
925 BMCWEB_LOG_DEBUG("{}: Dump creation task completed",
926 createdObjPath.str);
927 taskData->state = "Completed";
928 return task::completed;
929 },
930 "type='signal',interface='org.freedesktop.DBus.Properties',"
931 "member='PropertiesChanged',path='" +
932 createdObjPath.str + "'");
933
934 // The task timer is set to max time limit within which the
935 // requested dump will be collected.
936 task->startTimer(std::chrono::minutes(6));
937 task->payload.emplace(payload);
938 task->populateResp(asyncResp->res);
939 },
940 "xyz.openbmc_project.Dump.Manager", createdObjPath,
941 "org.freedesktop.DBus.Introspectable", "Introspect");
942 }
943
createDump(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req,const std::string & dumpType)944 inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
945 const crow::Request& req, const std::string& dumpType)
946 {
947 std::string dumpPath = getDumpEntriesPath(dumpType);
948 if (dumpPath.empty())
949 {
950 messages::internalError(asyncResp->res);
951 return;
952 }
953
954 std::optional<std::string> diagnosticDataType;
955 std::optional<std::string> oemDiagnosticDataType;
956
957 if (!redfish::json_util::readJsonAction( //
958 req, asyncResp->res, //
959 "DiagnosticDataType", diagnosticDataType, //
960 "OEMDiagnosticDataType", oemDiagnosticDataType //
961 ))
962 {
963 return;
964 }
965
966 if (dumpType == "System")
967 {
968 if (!oemDiagnosticDataType || !diagnosticDataType)
969 {
970 BMCWEB_LOG_ERROR(
971 "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!");
972 messages::actionParameterMissing(
973 asyncResp->res, "CollectDiagnosticData",
974 "DiagnosticDataType & OEMDiagnosticDataType");
975 return;
976 }
977 if ((*oemDiagnosticDataType != "System") ||
978 (*diagnosticDataType != "OEM"))
979 {
980 BMCWEB_LOG_ERROR("Wrong parameter values passed");
981 messages::internalError(asyncResp->res);
982 return;
983 }
984 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump/",
985 BMCWEB_REDFISH_SYSTEM_URI_NAME);
986 }
987 else if (dumpType == "BMC")
988 {
989 if (!diagnosticDataType)
990 {
991 BMCWEB_LOG_ERROR(
992 "CreateDump action parameter 'DiagnosticDataType' not found!");
993 messages::actionParameterMissing(
994 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
995 return;
996 }
997 if (*diagnosticDataType != "Manager")
998 {
999 BMCWEB_LOG_ERROR(
1000 "Wrong parameter value passed for 'DiagnosticDataType'");
1001 messages::internalError(asyncResp->res);
1002 return;
1003 }
1004 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump/",
1005 BMCWEB_REDFISH_MANAGER_URI_NAME);
1006 }
1007 else
1008 {
1009 BMCWEB_LOG_ERROR("CreateDump failed. Unknown dump type");
1010 messages::internalError(asyncResp->res);
1011 return;
1012 }
1013
1014 std::vector<std::pair<std::string, std::variant<std::string, uint64_t>>>
1015 createDumpParamVec;
1016
1017 if (req.session != nullptr)
1018 {
1019 createDumpParamVec.emplace_back(
1020 "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorId",
1021 req.session->clientIp);
1022 createDumpParamVec.emplace_back(
1023 "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorType",
1024 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client");
1025 }
1026
1027 dbus::utility::async_method_call(
1028 asyncResp,
1029 [asyncResp, payload(task::Payload(req)),
1030 dumpPath](const boost::system::error_code& ec,
1031 const sdbusplus::message_t& msg,
1032 const sdbusplus::message::object_path& objPath) mutable {
1033 if (ec)
1034 {
1035 BMCWEB_LOG_ERROR("CreateDump resp_handler got error {}", ec);
1036 const sd_bus_error* dbusError = msg.get_error();
1037 if (dbusError == nullptr)
1038 {
1039 messages::internalError(asyncResp->res);
1040 return;
1041 }
1042
1043 BMCWEB_LOG_ERROR("CreateDump DBus error: {} and error msg: {}",
1044 dbusError->name, dbusError->message);
1045 if (std::string_view(
1046 "xyz.openbmc_project.Common.Error.NotAllowed") ==
1047 dbusError->name)
1048 {
1049 messages::resourceInStandby(asyncResp->res);
1050 return;
1051 }
1052 if (std::string_view(
1053 "xyz.openbmc_project.Dump.Create.Error.Disabled") ==
1054 dbusError->name)
1055 {
1056 messages::serviceDisabled(asyncResp->res, dumpPath);
1057 return;
1058 }
1059 if (std::string_view(
1060 "xyz.openbmc_project.Common.Error.Unavailable") ==
1061 dbusError->name)
1062 {
1063 messages::resourceInUse(asyncResp->res);
1064 return;
1065 }
1066 // Other Dbus errors such as:
1067 // xyz.openbmc_project.Common.Error.InvalidArgument &
1068 // org.freedesktop.DBus.Error.InvalidArgs are all related to
1069 // the dbus call that is made here in the bmcweb
1070 // implementation and has nothing to do with the client's
1071 // input in the request. Hence, returning internal error
1072 // back to the client.
1073 messages::internalError(asyncResp->res);
1074 return;
1075 }
1076 BMCWEB_LOG_DEBUG("Dump Created. Path: {}", objPath.str);
1077 createDumpTaskCallback(std::move(payload), asyncResp, objPath);
1078 },
1079 "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType),
1080 "xyz.openbmc_project.Dump.Create", "CreateDump", createDumpParamVec);
1081 }
1082
clearDump(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)1083 inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1084 const std::string& dumpType)
1085 {
1086 dbus::utility::async_method_call(
1087 asyncResp,
1088 [asyncResp](const boost::system::error_code& ec) {
1089 if (ec)
1090 {
1091 BMCWEB_LOG_ERROR("clearDump resp_handler got error {}", ec);
1092 messages::internalError(asyncResp->res);
1093 return;
1094 }
1095 messages::success(asyncResp->res);
1096 },
1097 "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType),
1098 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1099 }
1100
parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap & params,std::string & filename,std::string & timestamp,std::string & logfile)1101 inline void parseCrashdumpParameters(
1102 const dbus::utility::DBusPropertiesMap& params, std::string& filename,
1103 std::string& timestamp, std::string& logfile)
1104 {
1105 const std::string* filenamePtr = nullptr;
1106 const std::string* timestampPtr = nullptr;
1107 const std::string* logfilePtr = nullptr;
1108
1109 const bool success = sdbusplus::unpackPropertiesNoThrow(
1110 dbus_utils::UnpackErrorPrinter(), params, "Timestamp", timestampPtr,
1111 "Filename", filenamePtr, "Log", logfilePtr);
1112
1113 if (!success)
1114 {
1115 return;
1116 }
1117
1118 if (filenamePtr != nullptr)
1119 {
1120 filename = *filenamePtr;
1121 }
1122
1123 if (timestampPtr != nullptr)
1124 {
1125 timestamp = *timestampPtr;
1126 }
1127
1128 if (logfilePtr != nullptr)
1129 {
1130 logfile = *logfilePtr;
1131 }
1132 }
1133
requestRoutesSystemLogServiceCollection(App & app)1134 inline void requestRoutesSystemLogServiceCollection(App& app)
1135 {
1136 /**
1137 * Functions triggers appropriate requests on DBus
1138 */
1139 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/")
1140 .privileges(redfish::privileges::getLogServiceCollection)
1141 .methods(
1142 boost::beast::http::verb::
1143 get)([&app](const crow::Request& req,
1144 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1145 const std::string& systemName) {
1146 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1147 {
1148 return;
1149 }
1150 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1151 {
1152 // Option currently returns no systems. TBD
1153 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1154 systemName);
1155 return;
1156 }
1157 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1158 {
1159 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1160 systemName);
1161 return;
1162 }
1163
1164 // Collections don't include the static data added by SubRoute
1165 // because it has a duplicate entry for members
1166 asyncResp->res.jsonValue["@odata.type"] =
1167 "#LogServiceCollection.LogServiceCollection";
1168 asyncResp->res.jsonValue["@odata.id"] =
1169 std::format("/redfish/v1/Systems/{}/LogServices",
1170 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1171 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
1172 asyncResp->res.jsonValue["Description"] =
1173 "Collection of LogServices for this Computer System";
1174 nlohmann::json& logServiceArray =
1175 asyncResp->res.jsonValue["Members"];
1176 logServiceArray = nlohmann::json::array();
1177 nlohmann::json::object_t eventLog;
1178 eventLog["@odata.id"] =
1179 std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
1180 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1181 logServiceArray.emplace_back(std::move(eventLog));
1182 if constexpr (BMCWEB_REDFISH_DUMP_LOG)
1183 {
1184 nlohmann::json::object_t dumpLog;
1185 dumpLog["@odata.id"] =
1186 std::format("/redfish/v1/Systems/{}/LogServices/Dump",
1187 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1188 logServiceArray.emplace_back(std::move(dumpLog));
1189 }
1190
1191 if constexpr (BMCWEB_REDFISH_CPU_LOG)
1192 {
1193 nlohmann::json::object_t crashdump;
1194 crashdump["@odata.id"] =
1195 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
1196 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1197 logServiceArray.emplace_back(std::move(crashdump));
1198 }
1199
1200 if constexpr (BMCWEB_REDFISH_HOST_LOGGER)
1201 {
1202 nlohmann::json::object_t hostlogger;
1203 hostlogger["@odata.id"] =
1204 std::format("/redfish/v1/Systems/{}/LogServices/HostLogger",
1205 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1206 logServiceArray.emplace_back(std::move(hostlogger));
1207 }
1208 asyncResp->res.jsonValue["Members@odata.count"] =
1209 logServiceArray.size();
1210
1211 constexpr std::array<std::string_view, 1> interfaces = {
1212 "xyz.openbmc_project.State.Boot.PostCode"};
1213 dbus::utility::getSubTreePaths(
1214 "/", 0, interfaces,
1215 [asyncResp](const boost::system::error_code& ec,
1216 const dbus::utility::MapperGetSubTreePathsResponse&
1217 subtreePath) {
1218 if (ec)
1219 {
1220 BMCWEB_LOG_ERROR("{}", ec);
1221 return;
1222 }
1223
1224 for (const auto& pathStr : subtreePath)
1225 {
1226 if (pathStr.find("PostCode") != std::string::npos)
1227 {
1228 nlohmann::json& logServiceArrayLocal =
1229 asyncResp->res.jsonValue["Members"];
1230 nlohmann::json::object_t member;
1231 member["@odata.id"] = std::format(
1232 "/redfish/v1/Systems/{}/LogServices/PostCodes",
1233 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1234
1235 logServiceArrayLocal.emplace_back(
1236 std::move(member));
1237
1238 asyncResp->res.jsonValue["Members@odata.count"] =
1239 logServiceArrayLocal.size();
1240 return;
1241 }
1242 }
1243 });
1244 });
1245 }
1246
requestRoutesEventLogService(App & app)1247 inline void requestRoutesEventLogService(App& app)
1248 {
1249 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/")
1250 .privileges(redfish::privileges::getLogService)
1251 .methods(
1252 boost::beast::http::verb::
1253 get)([&app](const crow::Request& req,
1254 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1255 const std::string& systemName) {
1256 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1257 {
1258 return;
1259 }
1260 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1261 {
1262 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1263 systemName);
1264 return;
1265 }
1266 asyncResp->res.jsonValue["@odata.id"] =
1267 std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
1268 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1269 asyncResp->res.jsonValue["@odata.type"] =
1270 "#LogService.v1_2_0.LogService";
1271 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1272 asyncResp->res.jsonValue["Description"] =
1273 "System Event Log Service";
1274 asyncResp->res.jsonValue["Id"] = "EventLog";
1275 asyncResp->res.jsonValue["OverWritePolicy"] =
1276 log_service::OverWritePolicy::WrapsWhenFull;
1277
1278 std::pair<std::string, std::string> redfishDateTimeOffset =
1279 redfish::time_utils::getDateTimeOffsetNow();
1280
1281 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1282 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1283 redfishDateTimeOffset.second;
1284
1285 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
1286 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1287 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1288 asyncResp->res
1289 .jsonValue["Actions"]["#LogService.ClearLog"]["target"]
1290
1291 = std::format(
1292 "/redfish/v1/Systems/{}/LogServices/EventLog/Actions/LogService.ClearLog",
1293 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1294 });
1295 }
1296
handleSystemsLogServicesEventLogActionsClearPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1297 inline void handleSystemsLogServicesEventLogActionsClearPost(
1298 App& app, const crow::Request& req,
1299 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1300 const std::string& systemName)
1301 {
1302 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1303 {
1304 return;
1305 }
1306 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1307 {
1308 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1309 systemName);
1310 return;
1311 }
1312
1313 // Clear the EventLog by deleting the log files
1314 std::vector<std::filesystem::path> redfishLogFiles;
1315 if (getRedfishLogFiles(redfishLogFiles))
1316 {
1317 for (const std::filesystem::path& file : redfishLogFiles)
1318 {
1319 std::error_code ec;
1320 std::filesystem::remove(file, ec);
1321 }
1322 }
1323
1324 // Reload rsyslog so it knows to start new log files
1325 dbus::utility::async_method_call(
1326 asyncResp,
1327 [asyncResp](const boost::system::error_code& ec) {
1328 if (ec)
1329 {
1330 BMCWEB_LOG_ERROR("Failed to reload rsyslog: {}", ec);
1331 messages::internalError(asyncResp->res);
1332 return;
1333 }
1334
1335 messages::success(asyncResp->res);
1336 },
1337 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1338 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1339 "replace");
1340 }
1341
requestRoutesJournalEventLogClear(App & app)1342 inline void requestRoutesJournalEventLogClear(App& app)
1343 {
1344 BMCWEB_ROUTE(
1345 app,
1346 "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
1347 .privileges(redfish::privileges::
1348 postLogServiceSubOverComputerSystemLogServiceCollection)
1349 .methods(boost::beast::http::verb::post)(std::bind_front(
1350 handleSystemsLogServicesEventLogActionsClearPost, std::ref(app)));
1351 }
1352
1353 enum class LogParseError
1354 {
1355 success,
1356 parseFailed,
1357 messageIdNotInRegistry,
1358 };
1359
fillEventLogEntryJson(const std::string & logEntryID,const std::string & logEntry,nlohmann::json::object_t & logEntryJson)1360 static LogParseError fillEventLogEntryJson(
1361 const std::string& logEntryID, const std::string& logEntry,
1362 nlohmann::json::object_t& logEntryJson)
1363 {
1364 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
1365 // First get the Timestamp
1366 size_t space = logEntry.find_first_of(' ');
1367 if (space == std::string::npos)
1368 {
1369 return LogParseError::parseFailed;
1370 }
1371 std::string timestamp = logEntry.substr(0, space);
1372 // Then get the log contents
1373 size_t entryStart = logEntry.find_first_not_of(' ', space);
1374 if (entryStart == std::string::npos)
1375 {
1376 return LogParseError::parseFailed;
1377 }
1378 std::string_view entry(logEntry);
1379 entry.remove_prefix(entryStart);
1380 // Use split to separate the entry into its fields
1381 std::vector<std::string> logEntryFields;
1382 bmcweb::split(logEntryFields, entry, ',');
1383 // We need at least a MessageId to be valid
1384 auto logEntryIter = logEntryFields.begin();
1385 if (logEntryIter == logEntryFields.end())
1386 {
1387 return LogParseError::parseFailed;
1388 }
1389 std::string& messageID = *logEntryIter;
1390 // Get the Message from the MessageRegistry
1391 const registries::Message* message = registries::getMessage(messageID);
1392
1393 logEntryIter++;
1394 if (message == nullptr)
1395 {
1396 BMCWEB_LOG_WARNING("Log entry not found in registry: {}", logEntry);
1397 return LogParseError::messageIdNotInRegistry;
1398 }
1399
1400 std::vector<std::string_view> messageArgs(logEntryIter,
1401 logEntryFields.end());
1402 messageArgs.resize(message->numberOfArgs);
1403
1404 std::string msg =
1405 redfish::registries::fillMessageArgs(messageArgs, message->message);
1406 if (msg.empty())
1407 {
1408 return LogParseError::parseFailed;
1409 }
1410
1411 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1412 // format which matches the Redfish format except for the fractional seconds
1413 // between the '.' and the '+', so just remove them.
1414 std::size_t dot = timestamp.find_first_of('.');
1415 std::size_t plus = timestamp.find_first_of('+');
1416 if (dot != std::string::npos && plus != std::string::npos)
1417 {
1418 timestamp.erase(dot, plus - dot);
1419 }
1420
1421 // Fill in the log entry with the gathered data
1422 logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1423 logEntryJson["@odata.id"] = boost::urls::format(
1424 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}",
1425 BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID);
1426 logEntryJson["Name"] = "System Event Log Entry";
1427 logEntryJson["Id"] = logEntryID;
1428 logEntryJson["Message"] = std::move(msg);
1429 logEntryJson["MessageId"] = std::move(messageID);
1430 logEntryJson["MessageArgs"] = messageArgs;
1431 logEntryJson["EntryType"] = "Event";
1432 logEntryJson["Severity"] = message->messageSeverity;
1433 logEntryJson["Created"] = std::move(timestamp);
1434 return LogParseError::success;
1435 }
1436
fillEventLogLogEntryFromDbusLogEntry(const DbusEventLogEntry & entry,nlohmann::json & objectToFillOut)1437 inline void fillEventLogLogEntryFromDbusLogEntry(
1438 const DbusEventLogEntry& entry, nlohmann::json& objectToFillOut)
1439 {
1440 objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1441 objectToFillOut["@odata.id"] = boost::urls::format(
1442 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}",
1443 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id));
1444 objectToFillOut["Name"] = "System Event Log Entry";
1445 objectToFillOut["Id"] = std::to_string(entry.Id);
1446 objectToFillOut["Message"] = entry.Message;
1447 objectToFillOut["Resolved"] = entry.Resolved;
1448 std::optional<bool> notifyAction =
1449 getProviderNotifyAction(entry.ServiceProviderNotify);
1450 if (notifyAction)
1451 {
1452 objectToFillOut["ServiceProviderNotified"] = *notifyAction;
1453 }
1454 if ((entry.Resolution != nullptr) && !entry.Resolution->empty())
1455 {
1456 objectToFillOut["Resolution"] = *entry.Resolution;
1457 }
1458 objectToFillOut["EntryType"] = "Event";
1459 objectToFillOut["Severity"] =
1460 translateSeverityDbusToRedfish(entry.Severity);
1461 objectToFillOut["Created"] =
1462 redfish::time_utils::getDateTimeUintMs(entry.Timestamp);
1463 objectToFillOut["Modified"] =
1464 redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp);
1465 if (entry.Path != nullptr)
1466 {
1467 objectToFillOut["AdditionalDataURI"] = boost::urls::format(
1468 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}/attachment",
1469 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id));
1470 }
1471 }
1472
afterLogEntriesGetManagedObjects(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::ManagedObjectType & resp)1473 inline void afterLogEntriesGetManagedObjects(
1474 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1475 const boost::system::error_code& ec,
1476 const dbus::utility::ManagedObjectType& resp)
1477 {
1478 if (ec)
1479 {
1480 // TODO Handle for specific error code
1481 BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}",
1482 ec);
1483 messages::internalError(asyncResp->res);
1484 return;
1485 }
1486 nlohmann::json::array_t entriesArray;
1487 for (const auto& objectPath : resp)
1488 {
1489 dbus::utility::DBusPropertiesMap propsFlattened;
1490 auto isEntry =
1491 std::ranges::find_if(objectPath.second, [](const auto& object) {
1492 return object.first == "xyz.openbmc_project.Logging.Entry";
1493 });
1494 if (isEntry == objectPath.second.end())
1495 {
1496 continue;
1497 }
1498
1499 for (const auto& interfaceMap : objectPath.second)
1500 {
1501 for (const auto& propertyMap : interfaceMap.second)
1502 {
1503 propsFlattened.emplace_back(propertyMap.first,
1504 propertyMap.second);
1505 }
1506 }
1507 std::optional<DbusEventLogEntry> optEntry =
1508 fillDbusEventLogEntryFromPropertyMap(propsFlattened);
1509
1510 if (!optEntry.has_value())
1511 {
1512 messages::internalError(asyncResp->res);
1513 return;
1514 }
1515 fillEventLogLogEntryFromDbusLogEntry(*optEntry,
1516 entriesArray.emplace_back());
1517 }
1518
1519 redfish::json_util::sortJsonArrayByKey(entriesArray, "Id");
1520 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
1521 asyncResp->res.jsonValue["Members"] = std::move(entriesArray);
1522 }
1523
handleSystemsLogServiceEventLogLogEntryCollection(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1524 inline void handleSystemsLogServiceEventLogLogEntryCollection(
1525 App& app, const crow::Request& req,
1526 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1527 const std::string& systemName)
1528 {
1529 query_param::QueryCapabilities capabilities = {
1530 .canDelegateTop = true,
1531 .canDelegateSkip = true,
1532 };
1533 query_param::Query delegatedQuery;
1534 if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
1535 delegatedQuery, capabilities))
1536 {
1537 return;
1538 }
1539 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1540 {
1541 // Option currently returns no systems. TBD
1542 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1543 systemName);
1544 return;
1545 }
1546 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1547 {
1548 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1549 systemName);
1550 return;
1551 }
1552
1553 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
1554 size_t skip = delegatedQuery.skip.value_or(0);
1555
1556 // Collections don't include the static data added by SubRoute
1557 // because it has a duplicate entry for members
1558 asyncResp->res.jsonValue["@odata.type"] =
1559 "#LogEntryCollection.LogEntryCollection";
1560 asyncResp->res.jsonValue["@odata.id"] =
1561 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1562 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1563 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1564 asyncResp->res.jsonValue["Description"] =
1565 "Collection of System Event Log Entries";
1566
1567 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1568 logEntryArray = nlohmann::json::array();
1569 // Go through the log files and create a unique ID for each
1570 // entry
1571 std::vector<std::filesystem::path> redfishLogFiles;
1572 getRedfishLogFiles(redfishLogFiles);
1573 uint64_t entryCount = 0;
1574 std::string logEntry;
1575
1576 // Oldest logs are in the last file, so start there and loop
1577 // backwards
1578 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
1579 {
1580 std::ifstream logStream(*it);
1581 if (!logStream.is_open())
1582 {
1583 continue;
1584 }
1585
1586 // Reset the unique ID on the first entry
1587 bool firstEntry = true;
1588 while (std::getline(logStream, logEntry))
1589 {
1590 std::string idStr;
1591 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1592 {
1593 continue;
1594 }
1595 firstEntry = false;
1596
1597 nlohmann::json::object_t bmcLogEntry;
1598 LogParseError status =
1599 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1600 if (status == LogParseError::messageIdNotInRegistry)
1601 {
1602 continue;
1603 }
1604 if (status != LogParseError::success)
1605 {
1606 messages::internalError(asyncResp->res);
1607 return;
1608 }
1609
1610 entryCount++;
1611 // Handle paging using skip (number of entries to skip from the
1612 // start) and top (number of entries to display)
1613 if (entryCount <= skip || entryCount > skip + top)
1614 {
1615 continue;
1616 }
1617
1618 logEntryArray.emplace_back(std::move(bmcLogEntry));
1619 }
1620 }
1621 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1622 if (skip + top < entryCount)
1623 {
1624 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1625 boost::urls::format(
1626 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries?$skip={}",
1627 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(skip + top));
1628 }
1629 }
1630
requestRoutesJournalEventLogEntryCollection(App & app)1631 inline void requestRoutesJournalEventLogEntryCollection(App& app)
1632 {
1633 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1634 .privileges(redfish::privileges::getLogEntryCollection)
1635 .methods(boost::beast::http::verb::get)(std::bind_front(
1636 handleSystemsLogServiceEventLogLogEntryCollection, std::ref(app)));
1637 }
1638
handleSystemsLogServiceEventLogEntriesGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & param)1639 inline void handleSystemsLogServiceEventLogEntriesGet(
1640 App& app, const crow::Request& req,
1641 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1642 const std::string& systemName, const std::string& param)
1643 {
1644 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1645 {
1646 return;
1647 }
1648 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1649 {
1650 // Option currently returns no systems. TBD
1651 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1652 systemName);
1653 return;
1654 }
1655
1656 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1657 {
1658 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1659 systemName);
1660 return;
1661 }
1662
1663 const std::string& targetID = param;
1664
1665 // Go through the log files and check the unique ID for each
1666 // entry to find the target entry
1667 std::vector<std::filesystem::path> redfishLogFiles;
1668 getRedfishLogFiles(redfishLogFiles);
1669 std::string logEntry;
1670
1671 // Oldest logs are in the last file, so start there and loop
1672 // backwards
1673 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
1674 {
1675 std::ifstream logStream(*it);
1676 if (!logStream.is_open())
1677 {
1678 continue;
1679 }
1680
1681 // Reset the unique ID on the first entry
1682 bool firstEntry = true;
1683 while (std::getline(logStream, logEntry))
1684 {
1685 std::string idStr;
1686 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1687 {
1688 continue;
1689 }
1690 firstEntry = false;
1691
1692 if (idStr == targetID)
1693 {
1694 nlohmann::json::object_t bmcLogEntry;
1695 LogParseError status =
1696 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1697 if (status != LogParseError::success)
1698 {
1699 messages::internalError(asyncResp->res);
1700 return;
1701 }
1702 asyncResp->res.jsonValue.update(bmcLogEntry);
1703 return;
1704 }
1705 }
1706 }
1707 // Requested ID was not found
1708 messages::resourceNotFound(asyncResp->res, "LogEntry", targetID);
1709 }
1710
requestRoutesJournalEventLogEntry(App & app)1711 inline void requestRoutesJournalEventLogEntry(App& app)
1712 {
1713 BMCWEB_ROUTE(
1714 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1715 .privileges(redfish::privileges::getLogEntry)
1716 .methods(boost::beast::http::verb::get)(std::bind_front(
1717 handleSystemsLogServiceEventLogEntriesGet, std::ref(app)));
1718 }
1719
dBusEventLogEntryCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1720 inline void dBusEventLogEntryCollection(
1721 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1722 {
1723 // Collections don't include the static data added by SubRoute
1724 // because it has a duplicate entry for members
1725 asyncResp->res.jsonValue["@odata.type"] =
1726 "#LogEntryCollection.LogEntryCollection";
1727 asyncResp->res.jsonValue["@odata.id"] =
1728 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1729 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1730 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1731 asyncResp->res.jsonValue["Description"] =
1732 "Collection of System Event Log Entries";
1733
1734 // DBus implementation of EventLog/Entries
1735 // Make call to Logging Service to find all log entry objects
1736 sdbusplus::message::object_path path("/xyz/openbmc_project/logging");
1737 dbus::utility::getManagedObjects(
1738 "xyz.openbmc_project.Logging", path,
1739 [asyncResp](const boost::system::error_code& ec,
1740 const dbus::utility::ManagedObjectType& resp) {
1741 afterLogEntriesGetManagedObjects(asyncResp, ec, resp);
1742 });
1743 }
1744
requestRoutesDBusEventLogEntryCollection(App & app)1745 inline void requestRoutesDBusEventLogEntryCollection(App& app)
1746 {
1747 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1748 .privileges(redfish::privileges::getLogEntryCollection)
1749 .methods(boost::beast::http::verb::get)(
1750 [&app](const crow::Request& req,
1751 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1752 const std::string& systemName) {
1753 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1754 {
1755 return;
1756 }
1757 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1758 {
1759 // Option currently returns no systems. TBD
1760 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1761 systemName);
1762 return;
1763 }
1764 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1765 {
1766 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1767 systemName);
1768 return;
1769 }
1770 dBusEventLogEntryCollection(asyncResp);
1771 });
1772 }
1773
afterDBusEventLogEntryGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const boost::system::error_code & ec,const dbus::utility::DBusPropertiesMap & resp)1774 inline void afterDBusEventLogEntryGet(
1775 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1776 const std::string& entryID, const boost::system::error_code& ec,
1777 const dbus::utility::DBusPropertiesMap& resp)
1778 {
1779 if (ec.value() == EBADR)
1780 {
1781 messages::resourceNotFound(asyncResp->res, "EventLogEntry", entryID);
1782 return;
1783 }
1784 if (ec)
1785 {
1786 BMCWEB_LOG_ERROR("EventLogEntry (DBus) resp_handler got error {}", ec);
1787 messages::internalError(asyncResp->res);
1788 return;
1789 }
1790
1791 std::optional<DbusEventLogEntry> optEntry =
1792 fillDbusEventLogEntryFromPropertyMap(resp);
1793
1794 if (!optEntry.has_value())
1795 {
1796 messages::internalError(asyncResp->res);
1797 return;
1798 }
1799
1800 fillEventLogLogEntryFromDbusLogEntry(*optEntry, asyncResp->res.jsonValue);
1801 }
1802
dBusEventLogEntryGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string entryID)1803 inline void dBusEventLogEntryGet(
1804 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
1805 {
1806 dbus::utility::escapePathForDbus(entryID);
1807
1808 // DBus implementation of EventLog/Entries
1809 // Make call to Logging Service to find all log entry objects
1810 dbus::utility::getAllProperties(
1811 "xyz.openbmc_project.Logging",
1812 "/xyz/openbmc_project/logging/entry/" + entryID, "",
1813 std::bind_front(afterDBusEventLogEntryGet, asyncResp, entryID));
1814 }
1815
dBusEventLogEntryPatch(const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryId)1816 inline void dBusEventLogEntryPatch(
1817 const crow::Request& req,
1818 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1819 const std::string& entryId)
1820 {
1821 std::optional<bool> resolved;
1822
1823 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved))
1824 {
1825 return;
1826 }
1827 BMCWEB_LOG_DEBUG("Set Resolved");
1828
1829 setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging",
1830 "/xyz/openbmc_project/logging/entry/" + entryId,
1831 "xyz.openbmc_project.Logging.Entry", "Resolved",
1832 resolved.value_or(false));
1833 }
1834
dBusEventLogEntryDelete(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string entryID)1835 inline void dBusEventLogEntryDelete(
1836 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
1837 {
1838 BMCWEB_LOG_DEBUG("Do delete single event entries.");
1839
1840 dbus::utility::escapePathForDbus(entryID);
1841
1842 // Process response from Logging service.
1843 auto respHandler = [asyncResp,
1844 entryID](const boost::system::error_code& ec) {
1845 BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done");
1846 if (ec)
1847 {
1848 if (ec.value() == EBADR)
1849 {
1850 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
1851 return;
1852 }
1853 // TODO Handle for specific error code
1854 BMCWEB_LOG_ERROR(
1855 "EventLogEntry (DBus) doDelete respHandler got error {}", ec);
1856 asyncResp->res.result(
1857 boost::beast::http::status::internal_server_error);
1858 return;
1859 }
1860
1861 asyncResp->res.result(boost::beast::http::status::ok);
1862 };
1863
1864 // Make call to Logging service to request Delete Log
1865 dbus::utility::async_method_call(
1866 asyncResp, respHandler, "xyz.openbmc_project.Logging",
1867 "/xyz/openbmc_project/logging/entry/" + entryID,
1868 "xyz.openbmc_project.Object.Delete", "Delete");
1869 }
1870
requestRoutesDBusEventLogEntry(App & app)1871 inline void requestRoutesDBusEventLogEntry(App& app)
1872 {
1873 BMCWEB_ROUTE(
1874 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1875 .privileges(redfish::privileges::getLogEntry)
1876 .methods(boost::beast::http::verb::get)(
1877 [&app](const crow::Request& req,
1878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1879 const std::string& systemName, const std::string& entryId) {
1880 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1881 {
1882 return;
1883 }
1884 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1885 {
1886 // Option currently returns no systems. TBD
1887 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1888 systemName);
1889 return;
1890 }
1891 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1892 {
1893 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1894 systemName);
1895 return;
1896 }
1897
1898 dBusEventLogEntryGet(asyncResp, entryId);
1899 });
1900
1901 BMCWEB_ROUTE(
1902 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1903 .privileges(redfish::privileges::patchLogEntry)
1904 .methods(boost::beast::http::verb::patch)(
1905 [&app](const crow::Request& req,
1906 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1907 const std::string& systemName, const std::string& entryId) {
1908 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1909 {
1910 return;
1911 }
1912 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1913 {
1914 // Option currently returns no systems. TBD
1915 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1916 systemName);
1917 return;
1918 }
1919 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1920 {
1921 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1922 systemName);
1923 return;
1924 }
1925
1926 dBusEventLogEntryPatch(req, asyncResp, entryId);
1927 });
1928
1929 BMCWEB_ROUTE(
1930 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1931 .privileges(
1932 redfish::privileges::
1933 deleteLogEntrySubOverComputerSystemLogServiceCollectionLogServiceLogEntryCollection)
1934 .methods(boost::beast::http::verb::delete_)(
1935 [&app](const crow::Request& req,
1936 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1937 const std::string& systemName, const std::string& param) {
1938 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1939 {
1940 return;
1941 }
1942 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1943 {
1944 // Option currently returns no systems. TBD
1945 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1946 systemName);
1947 return;
1948 }
1949 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1950 {
1951 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1952 systemName);
1953 return;
1954 }
1955 dBusEventLogEntryDelete(asyncResp, param);
1956 });
1957 }
1958
handleBMCLogServicesCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1959 inline void handleBMCLogServicesCollectionGet(
1960 crow::App& app, const crow::Request& req,
1961 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1962 const std::string& managerId)
1963 {
1964 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1965 {
1966 return;
1967 }
1968
1969 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1970 {
1971 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1972 return;
1973 }
1974
1975 // Collections don't include the static data added by SubRoute
1976 // because it has a duplicate entry for members
1977 asyncResp->res.jsonValue["@odata.type"] =
1978 "#LogServiceCollection.LogServiceCollection";
1979 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1980 "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME);
1981 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1982 asyncResp->res.jsonValue["Description"] =
1983 "Collection of LogServices for this Manager";
1984 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
1985 logServiceArray = nlohmann::json::array();
1986
1987 if constexpr (BMCWEB_REDFISH_BMC_JOURNAL)
1988 {
1989 nlohmann::json::object_t journal;
1990 journal["@odata.id"] =
1991 boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal",
1992 BMCWEB_REDFISH_MANAGER_URI_NAME);
1993 logServiceArray.emplace_back(std::move(journal));
1994 }
1995
1996 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
1997
1998 if constexpr (BMCWEB_REDFISH_DUMP_LOG)
1999 {
2000 constexpr std::array<std::string_view, 1> interfaces = {
2001 "xyz.openbmc_project.Collection.DeleteAll"};
2002 dbus::utility::getSubTreePaths(
2003 "/xyz/openbmc_project/dump", 0, interfaces,
2004 [asyncResp](const boost::system::error_code& ec,
2005 const dbus::utility::MapperGetSubTreePathsResponse&
2006 subTreePaths) {
2007 if (ec)
2008 {
2009 BMCWEB_LOG_ERROR(
2010 "handleBMCLogServicesCollectionGet respHandler got error {}",
2011 ec);
2012 // Assume that getting an error simply means there are no
2013 // dump LogServices. Return without adding any error
2014 // response.
2015 return;
2016 }
2017
2018 nlohmann::json& logServiceArrayLocal =
2019 asyncResp->res.jsonValue["Members"];
2020
2021 for (const std::string& path : subTreePaths)
2022 {
2023 if (path == "/xyz/openbmc_project/dump/bmc")
2024 {
2025 nlohmann::json::object_t member;
2026 member["@odata.id"] = boost::urls::format(
2027 "/redfish/v1/Managers/{}/LogServices/Dump",
2028 BMCWEB_REDFISH_MANAGER_URI_NAME);
2029 logServiceArrayLocal.emplace_back(std::move(member));
2030 }
2031 else if (path == "/xyz/openbmc_project/dump/faultlog")
2032 {
2033 nlohmann::json::object_t member;
2034 member["@odata.id"] = boost::urls::format(
2035 "/redfish/v1/Managers/{}/LogServices/FaultLog",
2036 BMCWEB_REDFISH_MANAGER_URI_NAME);
2037 logServiceArrayLocal.emplace_back(std::move(member));
2038 }
2039 }
2040
2041 asyncResp->res.jsonValue["Members@odata.count"] =
2042 logServiceArrayLocal.size();
2043 });
2044 }
2045 }
2046
requestRoutesBMCLogServiceCollection(App & app)2047 inline void requestRoutesBMCLogServiceCollection(App& app)
2048 {
2049 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/")
2050 .privileges(redfish::privileges::getLogServiceCollection)
2051 .methods(boost::beast::http::verb::get)(
2052 std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app)));
2053 }
2054
getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)2055 inline void getDumpServiceInfo(
2056 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2057 const std::string& dumpType)
2058 {
2059 std::string dumpPath;
2060 log_service::OverWritePolicy overWritePolicy =
2061 log_service::OverWritePolicy::Invalid;
2062 bool collectDiagnosticDataSupported = false;
2063
2064 if (dumpType == "BMC")
2065 {
2066 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump",
2067 BMCWEB_REDFISH_MANAGER_URI_NAME);
2068 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
2069 collectDiagnosticDataSupported = true;
2070 }
2071 else if (dumpType == "FaultLog")
2072 {
2073 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog",
2074 BMCWEB_REDFISH_MANAGER_URI_NAME);
2075 overWritePolicy = log_service::OverWritePolicy::Unknown;
2076 collectDiagnosticDataSupported = false;
2077 }
2078 else if (dumpType == "System")
2079 {
2080 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump",
2081 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2082 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
2083 collectDiagnosticDataSupported = true;
2084 }
2085 else
2086 {
2087 BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}",
2088 dumpType);
2089 messages::internalError(asyncResp->res);
2090 return;
2091 }
2092
2093 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2094 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2095 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2096 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2097 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2098 asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy;
2099
2100 std::pair<std::string, std::string> redfishDateTimeOffset =
2101 redfish::time_utils::getDateTimeOffsetNow();
2102 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2103 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2104 redfishDateTimeOffset.second;
2105
2106 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2107
2108 if (collectDiagnosticDataSupported)
2109 {
2110 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2111 ["target"] =
2112 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2113 }
2114
2115 constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface};
2116 dbus::utility::getSubTreePaths(
2117 "/xyz/openbmc_project/dump", 0, interfaces,
2118 [asyncResp, dumpType, dumpPath](
2119 const boost::system::error_code& ec,
2120 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2121 if (ec)
2122 {
2123 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}",
2124 ec);
2125 // Assume that getting an error simply means there are no dump
2126 // LogServices. Return without adding any error response.
2127 return;
2128 }
2129 std::string dbusDumpPath = getDumpPath(dumpType);
2130 for (const std::string& path : subTreePaths)
2131 {
2132 if (path == dbusDumpPath)
2133 {
2134 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
2135 ["target"] =
2136 dumpPath + "/Actions/LogService.ClearLog";
2137 break;
2138 }
2139 }
2140 });
2141 }
2142
handleLogServicesDumpServiceGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2143 inline void handleLogServicesDumpServiceGet(
2144 crow::App& app, const std::string& dumpType, const crow::Request& req,
2145 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2146 const std::string& managerId)
2147 {
2148 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2149 {
2150 return;
2151 }
2152
2153 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2154 {
2155 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2156 return;
2157 }
2158
2159 getDumpServiceInfo(asyncResp, dumpType);
2160 }
2161
handleLogServicesDumpServiceComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)2162 inline void handleLogServicesDumpServiceComputerSystemGet(
2163 crow::App& app, const crow::Request& req,
2164 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2165 const std::string& chassisId)
2166 {
2167 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2168 {
2169 return;
2170 }
2171 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2172 {
2173 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2174 return;
2175 }
2176 getDumpServiceInfo(asyncResp, "System");
2177 }
2178
handleLogServicesDumpEntriesCollectionGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2179 inline void handleLogServicesDumpEntriesCollectionGet(
2180 crow::App& app, const std::string& dumpType, const crow::Request& req,
2181 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2182 const std::string& managerId)
2183 {
2184 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2185 {
2186 return;
2187 }
2188
2189 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2190 {
2191 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2192 return;
2193 }
2194 getDumpEntryCollection(asyncResp, dumpType);
2195 }
2196
handleLogServicesDumpEntriesCollectionComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)2197 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet(
2198 crow::App& app, const crow::Request& req,
2199 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2200 const std::string& chassisId)
2201 {
2202 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2203 {
2204 return;
2205 }
2206 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2207 {
2208 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2209 return;
2210 }
2211 getDumpEntryCollection(asyncResp, "System");
2212 }
2213
handleLogServicesDumpEntryGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & dumpId)2214 inline void handleLogServicesDumpEntryGet(
2215 crow::App& app, const std::string& dumpType, const crow::Request& req,
2216 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2217 const std::string& managerId, const std::string& dumpId)
2218 {
2219 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2220 {
2221 return;
2222 }
2223 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2224 {
2225 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2226 return;
2227 }
2228 getDumpEntryById(asyncResp, dumpId, dumpType);
2229 }
2230
handleLogServicesDumpEntryComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & dumpId)2231 inline void handleLogServicesDumpEntryComputerSystemGet(
2232 crow::App& app, const crow::Request& req,
2233 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2234 const std::string& chassisId, const std::string& dumpId)
2235 {
2236 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2237 {
2238 return;
2239 }
2240 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2241 {
2242 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2243 return;
2244 }
2245 getDumpEntryById(asyncResp, dumpId, "System");
2246 }
2247
handleLogServicesDumpEntryDelete(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & dumpId)2248 inline void handleLogServicesDumpEntryDelete(
2249 crow::App& app, const std::string& dumpType, const crow::Request& req,
2250 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2251 const std::string& managerId, const std::string& dumpId)
2252 {
2253 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2254 {
2255 return;
2256 }
2257
2258 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2259 {
2260 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2261 return;
2262 }
2263 deleteDumpEntry(asyncResp, dumpId, dumpType);
2264 }
2265
handleLogServicesDumpEntryComputerSystemDelete(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & dumpId)2266 inline void handleLogServicesDumpEntryComputerSystemDelete(
2267 crow::App& app, const crow::Request& req,
2268 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2269 const std::string& chassisId, const std::string& dumpId)
2270 {
2271 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2272 {
2273 return;
2274 }
2275 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2276 {
2277 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2278 return;
2279 }
2280 deleteDumpEntry(asyncResp, dumpId, "System");
2281 }
2282
handleLogServicesDumpEntryDownloadGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId,const std::string & dumpId)2283 inline void handleLogServicesDumpEntryDownloadGet(
2284 crow::App& app, const std::string& dumpType, const crow::Request& req,
2285 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2286 const std::string& managerId, const std::string& dumpId)
2287 {
2288 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2289 {
2290 return;
2291 }
2292
2293 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2294 {
2295 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2296 return;
2297 }
2298 downloadDumpEntry(asyncResp, dumpId, dumpType);
2299 }
2300
handleDBusEventLogEntryDownloadGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & entryID)2301 inline void handleDBusEventLogEntryDownloadGet(
2302 crow::App& app, const std::string& dumpType, const crow::Request& req,
2303 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2304 const std::string& systemName, const std::string& entryID)
2305 {
2306 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2307 {
2308 return;
2309 }
2310 if (!http_helpers::isContentTypeAllowed(
2311 req.getHeaderValue("Accept"),
2312 http_helpers::ContentType::OctetStream, true))
2313 {
2314 asyncResp->res.result(boost::beast::http::status::bad_request);
2315 return;
2316 }
2317 downloadEventLogEntry(asyncResp, systemName, entryID, dumpType);
2318 }
2319
handleLogServicesDumpCollectDiagnosticDataPost(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2320 inline void handleLogServicesDumpCollectDiagnosticDataPost(
2321 crow::App& app, const std::string& dumpType, const crow::Request& req,
2322 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2323 const std::string& managerId)
2324 {
2325 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2326 {
2327 return;
2328 }
2329 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2330 {
2331 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2332 return;
2333 }
2334
2335 createDump(asyncResp, req, dumpType);
2336 }
2337
handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)2338 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(
2339 crow::App& app, const crow::Request& req,
2340 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2341 const std::string& systemName)
2342 {
2343 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2344 {
2345 return;
2346 }
2347
2348 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2349 {
2350 // Option currently returns no systems. TBD
2351 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2352 systemName);
2353 return;
2354 }
2355 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2356 {
2357 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2358 systemName);
2359 return;
2360 }
2361 createDump(asyncResp, req, "System");
2362 }
2363
handleLogServicesDumpClearLogPost(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2364 inline void handleLogServicesDumpClearLogPost(
2365 crow::App& app, const std::string& dumpType, const crow::Request& req,
2366 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2367 const std::string& managerId)
2368 {
2369 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2370 {
2371 return;
2372 }
2373
2374 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2375 {
2376 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2377 return;
2378 }
2379 clearDump(asyncResp, dumpType);
2380 }
2381
handleLogServicesDumpClearLogComputerSystemPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)2382 inline void handleLogServicesDumpClearLogComputerSystemPost(
2383 crow::App& app, const crow::Request& req,
2384 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2385 const std::string& systemName)
2386 {
2387 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2388 {
2389 return;
2390 }
2391 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2392 {
2393 // Option currently returns no systems. TBD
2394 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2395 systemName);
2396 return;
2397 }
2398 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2399 {
2400 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2401 systemName);
2402 return;
2403 }
2404 clearDump(asyncResp, "System");
2405 }
2406
requestRoutesBMCDumpService(App & app)2407 inline void requestRoutesBMCDumpService(App& app)
2408 {
2409 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/")
2410 .privileges(redfish::privileges::getLogService)
2411 .methods(boost::beast::http::verb::get)(std::bind_front(
2412 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
2413 }
2414
requestRoutesBMCDumpEntryCollection(App & app)2415 inline void requestRoutesBMCDumpEntryCollection(App& app)
2416 {
2417 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/")
2418 .privileges(redfish::privileges::getLogEntryCollection)
2419 .methods(boost::beast::http::verb::get)(std::bind_front(
2420 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
2421 }
2422
requestRoutesBMCDumpEntry(App & app)2423 inline void requestRoutesBMCDumpEntry(App& app)
2424 {
2425 BMCWEB_ROUTE(app,
2426 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
2427 .privileges(redfish::privileges::getLogEntry)
2428 .methods(boost::beast::http::verb::get)(std::bind_front(
2429 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2430
2431 BMCWEB_ROUTE(app,
2432 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
2433 .privileges(redfish::privileges::deleteLogEntry)
2434 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2435 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
2436 }
2437
requestRoutesBMCDumpEntryDownload(App & app)2438 inline void requestRoutesBMCDumpEntryDownload(App& app)
2439 {
2440 BMCWEB_ROUTE(
2441 app,
2442 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/")
2443 .privileges(redfish::privileges::getLogEntry)
2444 .methods(boost::beast::http::verb::get)(std::bind_front(
2445 handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC"));
2446 }
2447
requestRoutesBMCDumpCreate(App & app)2448 inline void requestRoutesBMCDumpCreate(App& app)
2449 {
2450 BMCWEB_ROUTE(
2451 app,
2452 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2453 .privileges(redfish::privileges::postLogService)
2454 .methods(boost::beast::http::verb::post)(
2455 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2456 std::ref(app), "BMC"));
2457 }
2458
requestRoutesBMCDumpClear(App & app)2459 inline void requestRoutesBMCDumpClear(App& app)
2460 {
2461 BMCWEB_ROUTE(
2462 app,
2463 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2464 .privileges(redfish::privileges::postLogService)
2465 .methods(boost::beast::http::verb::post)(std::bind_front(
2466 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2467 }
2468
requestRoutesDBusEventLogEntryDownload(App & app)2469 inline void requestRoutesDBusEventLogEntryDownload(App& app)
2470 {
2471 BMCWEB_ROUTE(
2472 app,
2473 "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment/")
2474 .privileges(redfish::privileges::getLogEntry)
2475 .methods(boost::beast::http::verb::get)(std::bind_front(
2476 handleDBusEventLogEntryDownloadGet, std::ref(app), "System"));
2477 }
2478
requestRoutesFaultLogDumpService(App & app)2479 inline void requestRoutesFaultLogDumpService(App& app)
2480 {
2481 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/")
2482 .privileges(redfish::privileges::getLogService)
2483 .methods(boost::beast::http::verb::get)(std::bind_front(
2484 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2485 }
2486
requestRoutesFaultLogDumpEntryCollection(App & app)2487 inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2488 {
2489 BMCWEB_ROUTE(app,
2490 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/")
2491 .privileges(redfish::privileges::getLogEntryCollection)
2492 .methods(boost::beast::http::verb::get)(
2493 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2494 std::ref(app), "FaultLog"));
2495 }
2496
requestRoutesFaultLogDumpEntry(App & app)2497 inline void requestRoutesFaultLogDumpEntry(App& app)
2498 {
2499 BMCWEB_ROUTE(
2500 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
2501 .privileges(redfish::privileges::getLogEntry)
2502 .methods(boost::beast::http::verb::get)(std::bind_front(
2503 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2504
2505 BMCWEB_ROUTE(
2506 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
2507 .privileges(redfish::privileges::deleteLogEntry)
2508 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2509 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2510 }
2511
requestRoutesFaultLogDumpClear(App & app)2512 inline void requestRoutesFaultLogDumpClear(App& app)
2513 {
2514 BMCWEB_ROUTE(
2515 app,
2516 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/")
2517 .privileges(redfish::privileges::postLogService)
2518 .methods(boost::beast::http::verb::post)(std::bind_front(
2519 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
2520 }
2521
requestRoutesSystemDumpService(App & app)2522 inline void requestRoutesSystemDumpService(App& app)
2523 {
2524 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/")
2525 .privileges(redfish::privileges::getLogService)
2526 .methods(boost::beast::http::verb::get)(std::bind_front(
2527 handleLogServicesDumpServiceComputerSystemGet, std::ref(app)));
2528 }
2529
requestRoutesSystemDumpEntryCollection(App & app)2530 inline void requestRoutesSystemDumpEntryCollection(App& app)
2531 {
2532 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/")
2533 .privileges(redfish::privileges::getLogEntryCollection)
2534 .methods(boost::beast::http::verb::get)(std::bind_front(
2535 handleLogServicesDumpEntriesCollectionComputerSystemGet,
2536 std::ref(app)));
2537 }
2538
requestRoutesSystemDumpEntry(App & app)2539 inline void requestRoutesSystemDumpEntry(App& app)
2540 {
2541 BMCWEB_ROUTE(app,
2542 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2543 .privileges(redfish::privileges::getLogEntry)
2544 .methods(boost::beast::http::verb::get)(std::bind_front(
2545 handleLogServicesDumpEntryComputerSystemGet, std::ref(app)));
2546
2547 BMCWEB_ROUTE(app,
2548 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2549 .privileges(redfish::privileges::deleteLogEntry)
2550 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2551 handleLogServicesDumpEntryComputerSystemDelete, std::ref(app)));
2552 }
2553
requestRoutesSystemDumpCreate(App & app)2554 inline void requestRoutesSystemDumpCreate(App& app)
2555 {
2556 BMCWEB_ROUTE(
2557 app,
2558 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2559 .privileges(redfish::privileges::
2560 postLogServiceSubOverComputerSystemLogServiceCollection)
2561 .methods(boost::beast::http::verb::post)(std::bind_front(
2562 handleLogServicesDumpCollectDiagnosticDataComputerSystemPost,
2563 std::ref(app)));
2564 }
2565
requestRoutesSystemDumpClear(App & app)2566 inline void requestRoutesSystemDumpClear(App& app)
2567 {
2568 BMCWEB_ROUTE(
2569 app,
2570 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2571 .privileges(redfish::privileges::
2572 postLogServiceSubOverComputerSystemLogServiceCollection)
2573 .methods(boost::beast::http::verb::post)(std::bind_front(
2574 handleLogServicesDumpClearLogComputerSystemPost, std::ref(app)));
2575 }
2576
requestRoutesCrashdumpService(App & app)2577 inline void requestRoutesCrashdumpService(App& app)
2578 {
2579 // Note: Deviated from redfish privilege registry for GET & HEAD
2580 // method for security reasons.
2581 /**
2582 * Functions triggers appropriate requests on DBus
2583 */
2584 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/")
2585 // This is incorrect, should be:
2586 //.privileges(redfish::privileges::getLogService)
2587 .privileges({{"ConfigureManager"}})
2588 .methods(
2589 boost::beast::http::verb::
2590 get)([&app](const crow::Request& req,
2591 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2592 const std::string& systemName) {
2593 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2594 {
2595 return;
2596 }
2597 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2598 {
2599 // Option currently returns no systems. TBD
2600 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2601 systemName);
2602 return;
2603 }
2604 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2605 {
2606 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2607 systemName);
2608 return;
2609 }
2610
2611 // Copy over the static data to include the entries added by
2612 // SubRoute
2613 asyncResp->res.jsonValue["@odata.id"] =
2614 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
2615 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2616 asyncResp->res.jsonValue["@odata.type"] =
2617 "#LogService.v1_2_0.LogService";
2618 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2619 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2620 asyncResp->res.jsonValue["Id"] = "Crashdump";
2621 asyncResp->res.jsonValue["OverWritePolicy"] =
2622 log_service::OverWritePolicy::WrapsWhenFull;
2623 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
2624
2625 std::pair<std::string, std::string> redfishDateTimeOffset =
2626 redfish::time_utils::getDateTimeOffsetNow();
2627 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2628 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2629 redfishDateTimeOffset.second;
2630
2631 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
2632 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
2633 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2634 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
2635 ["target"] = std::format(
2636 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog",
2637 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2638 asyncResp->res
2639 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2640 ["target"] = std::format(
2641 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData",
2642 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2643 });
2644 }
2645
requestRoutesCrashdumpClear(App & app)2646 void inline requestRoutesCrashdumpClear(App& app)
2647 {
2648 BMCWEB_ROUTE(
2649 app,
2650 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/")
2651 // This is incorrect, should be:
2652 //.privileges(redfish::privileges::postLogService)
2653 .privileges({{"ConfigureComponents"}})
2654 .methods(boost::beast::http::verb::post)(
2655 [&app](const crow::Request& req,
2656 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2657 const std::string& systemName) {
2658 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2659 {
2660 return;
2661 }
2662 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2663 {
2664 // Option currently returns no systems. TBD
2665 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2666 systemName);
2667 return;
2668 }
2669 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2670 {
2671 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2672 systemName);
2673 return;
2674 }
2675 dbus::utility::async_method_call(
2676 asyncResp,
2677 [asyncResp](const boost::system::error_code& ec,
2678 const std::string&) {
2679 if (ec)
2680 {
2681 messages::internalError(asyncResp->res);
2682 return;
2683 }
2684 messages::success(asyncResp->res);
2685 },
2686 crashdumpObject, crashdumpPath, deleteAllInterface,
2687 "DeleteAll");
2688 });
2689 }
2690
logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & logID,nlohmann::json & logEntryJson)2691 inline void logCrashdumpEntry(
2692 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2693 const std::string& logID, nlohmann::json& logEntryJson)
2694 {
2695 auto getStoredLogCallback =
2696 [asyncResp, logID,
2697 &logEntryJson](const boost::system::error_code& ec,
2698 const dbus::utility::DBusPropertiesMap& params) {
2699 if (ec)
2700 {
2701 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message());
2702 if (ec.value() ==
2703 boost::system::linux_error::bad_request_descriptor)
2704 {
2705 messages::resourceNotFound(asyncResp->res, "LogEntry",
2706 logID);
2707 }
2708 else
2709 {
2710 messages::internalError(asyncResp->res);
2711 }
2712 return;
2713 }
2714
2715 std::string timestamp{};
2716 std::string filename{};
2717 std::string logfile{};
2718 parseCrashdumpParameters(params, filename, timestamp, logfile);
2719
2720 if (filename.empty() || timestamp.empty())
2721 {
2722 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
2723 return;
2724 }
2725
2726 std::string crashdumpURI =
2727 std::format(
2728 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/",
2729 BMCWEB_REDFISH_SYSTEM_URI_NAME) +
2730 logID + "/" + filename;
2731 nlohmann::json::object_t logEntry;
2732 logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
2733 logEntry["@odata.id"] = boost::urls::format(
2734 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}",
2735 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID);
2736 logEntry["Name"] = "CPU Crashdump";
2737 logEntry["Id"] = logID;
2738 logEntry["EntryType"] = log_entry::LogEntryType::Oem;
2739 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2740 logEntry["DiagnosticDataType"] = "OEM";
2741 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2742 logEntry["Created"] = std::move(timestamp);
2743
2744 // If logEntryJson references an array of LogEntry resources
2745 // ('Members' list), then push this as a new entry, otherwise set it
2746 // directly
2747 if (logEntryJson.is_array())
2748 {
2749 logEntryJson.push_back(logEntry);
2750 asyncResp->res.jsonValue["Members@odata.count"] =
2751 logEntryJson.size();
2752 }
2753 else
2754 {
2755 logEntryJson.update(logEntry);
2756 }
2757 };
2758 dbus::utility::getAllProperties(
2759 crashdumpObject, crashdumpPath + std::string("/") + logID,
2760 crashdumpInterface, std::move(getStoredLogCallback));
2761 }
2762
requestRoutesCrashdumpEntryCollection(App & app)2763 inline void requestRoutesCrashdumpEntryCollection(App& app)
2764 {
2765 // Note: Deviated from redfish privilege registry for GET & HEAD
2766 // method for security reasons.
2767 /**
2768 * Functions triggers appropriate requests on DBus
2769 */
2770 BMCWEB_ROUTE(app,
2771 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/")
2772 // This is incorrect, should be.
2773 //.privileges(redfish::privileges::postLogEntryCollection)
2774 .privileges({{"ConfigureComponents"}})
2775 .methods(
2776 boost::beast::http::verb::
2777 get)([&app](const crow::Request& req,
2778 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2779 const std::string& systemName) {
2780 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2781 {
2782 return;
2783 }
2784 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2785 {
2786 // Option currently returns no systems. TBD
2787 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2788 systemName);
2789 return;
2790 }
2791 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2792 {
2793 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2794 systemName);
2795 return;
2796 }
2797
2798 constexpr std::array<std::string_view, 1> interfaces = {
2799 crashdumpInterface};
2800 dbus::utility::getSubTreePaths(
2801 "/", 0, interfaces,
2802 [asyncResp](const boost::system::error_code& ec,
2803 const std::vector<std::string>& resp) {
2804 if (ec)
2805 {
2806 if (ec.value() !=
2807 boost::system::errc::no_such_file_or_directory)
2808 {
2809 BMCWEB_LOG_DEBUG("failed to get entries ec: {}",
2810 ec.message());
2811 messages::internalError(asyncResp->res);
2812 return;
2813 }
2814 }
2815 asyncResp->res.jsonValue["@odata.type"] =
2816 "#LogEntryCollection.LogEntryCollection";
2817 asyncResp->res.jsonValue["@odata.id"] = std::format(
2818 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
2819 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2820 asyncResp->res.jsonValue["Name"] =
2821 "Open BMC Crashdump Entries";
2822 asyncResp->res.jsonValue["Description"] =
2823 "Collection of Crashdump Entries";
2824 asyncResp->res.jsonValue["Members"] =
2825 nlohmann::json::array();
2826 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2827
2828 for (const std::string& path : resp)
2829 {
2830 const sdbusplus::message::object_path objPath(path);
2831 // Get the log ID
2832 std::string logID = objPath.filename();
2833 if (logID.empty())
2834 {
2835 continue;
2836 }
2837 // Add the log entry to the array
2838 logCrashdumpEntry(asyncResp, logID,
2839 asyncResp->res.jsonValue["Members"]);
2840 }
2841 });
2842 });
2843 }
2844
requestRoutesCrashdumpEntry(App & app)2845 inline void requestRoutesCrashdumpEntry(App& app)
2846 {
2847 // Note: Deviated from redfish privilege registry for GET & HEAD
2848 // method for security reasons.
2849
2850 BMCWEB_ROUTE(
2851 app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/")
2852 // this is incorrect, should be
2853 // .privileges(redfish::privileges::getLogEntry)
2854 .privileges({{"ConfigureComponents"}})
2855 .methods(boost::beast::http::verb::get)(
2856 [&app](const crow::Request& req,
2857 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2858 const std::string& systemName, const std::string& param) {
2859 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2860 {
2861 return;
2862 }
2863 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2864 {
2865 // Option currently returns no systems. TBD
2866 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2867 systemName);
2868 return;
2869 }
2870 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2871 {
2872 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2873 systemName);
2874 return;
2875 }
2876 const std::string& logID = param;
2877 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2878 });
2879 }
2880
requestRoutesCrashdumpFile(App & app)2881 inline void requestRoutesCrashdumpFile(App& app)
2882 {
2883 // Note: Deviated from redfish privilege registry for GET & HEAD
2884 // method for security reasons.
2885 BMCWEB_ROUTE(
2886 app,
2887 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/")
2888 .privileges(redfish::privileges::getLogEntry)
2889 .methods(boost::beast::http::verb::get)(
2890 [](const crow::Request& req,
2891 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2892 const std::string& systemName, const std::string& logID,
2893 const std::string& fileName) {
2894 // Do not call getRedfishRoute here since the crashdump file is
2895 // not a Redfish resource.
2896
2897 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2898 {
2899 // Option currently returns no systems. TBD
2900 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2901 systemName);
2902 return;
2903 }
2904 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2905 {
2906 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2907 systemName);
2908 return;
2909 }
2910
2911 auto getStoredLogCallback =
2912 [asyncResp, logID, fileName,
2913 url(boost::urls::url(req.url()))](
2914 const boost::system::error_code& ec,
2915 const std::vector<std::pair<
2916 std::string, dbus::utility::DbusVariantType>>&
2917 resp) {
2918 if (ec)
2919 {
2920 BMCWEB_LOG_DEBUG("failed to get log ec: {}",
2921 ec.message());
2922 messages::internalError(asyncResp->res);
2923 return;
2924 }
2925
2926 std::string dbusFilename{};
2927 std::string dbusTimestamp{};
2928 std::string dbusFilepath{};
2929
2930 parseCrashdumpParameters(resp, dbusFilename,
2931 dbusTimestamp, dbusFilepath);
2932
2933 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2934 dbusFilepath.empty())
2935 {
2936 messages::resourceNotFound(asyncResp->res,
2937 "LogEntry", logID);
2938 return;
2939 }
2940
2941 // Verify the file name parameter is correct
2942 if (fileName != dbusFilename)
2943 {
2944 messages::resourceNotFound(asyncResp->res,
2945 "LogEntry", logID);
2946 return;
2947 }
2948
2949 if (asyncResp->res.openFile(dbusFilepath) !=
2950 crow::OpenCode::Success)
2951 {
2952 messages::resourceNotFound(asyncResp->res,
2953 "LogEntry", logID);
2954 return;
2955 }
2956
2957 // Configure this to be a file download when accessed
2958 // from a browser
2959 asyncResp->res.addHeader(
2960 boost::beast::http::field::content_disposition,
2961 "attachment");
2962 };
2963 dbus::utility::getAllProperties(
2964 *crow::connections::systemBus, crashdumpObject,
2965 crashdumpPath + std::string("/") + logID,
2966 crashdumpInterface, std::move(getStoredLogCallback));
2967 });
2968 }
2969
2970 enum class OEMDiagnosticType
2971 {
2972 onDemand,
2973 telemetry,
2974 invalid,
2975 };
2976
getOEMDiagnosticType(std::string_view oemDiagStr)2977 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr)
2978 {
2979 if (oemDiagStr == "OnDemand")
2980 {
2981 return OEMDiagnosticType::onDemand;
2982 }
2983 if (oemDiagStr == "Telemetry")
2984 {
2985 return OEMDiagnosticType::telemetry;
2986 }
2987
2988 return OEMDiagnosticType::invalid;
2989 }
2990
requestRoutesCrashdumpCollect(App & app)2991 inline void requestRoutesCrashdumpCollect(App& app)
2992 {
2993 // Note: Deviated from redfish privilege registry for GET & HEAD
2994 // method for security reasons.
2995 BMCWEB_ROUTE(
2996 app,
2997 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
2998 // The below is incorrect; Should be ConfigureManager
2999 //.privileges(redfish::privileges::postLogService)
3000 .privileges({{"ConfigureComponents"}})
3001 .methods(boost::beast::http::verb::post)(
3002 [&app](const crow::Request& req,
3003 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3004 const std::string& systemName) {
3005 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3006 {
3007 return;
3008 }
3009
3010 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
3011 {
3012 // Option currently returns no systems. TBD
3013 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3014 systemName);
3015 return;
3016 }
3017 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
3018 {
3019 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3020 systemName);
3021 return;
3022 }
3023
3024 std::string diagnosticDataType;
3025 std::string oemDiagnosticDataType;
3026 if (!redfish::json_util::readJsonAction( //
3027 req, asyncResp->res, //
3028 "DiagnosticDataType", diagnosticDataType, //
3029 "OEMDiagnosticDataType", oemDiagnosticDataType //
3030 ))
3031 {
3032 return;
3033 }
3034
3035 if (diagnosticDataType != "OEM")
3036 {
3037 BMCWEB_LOG_ERROR(
3038 "Only OEM DiagnosticDataType supported for Crashdump");
3039 messages::actionParameterValueFormatError(
3040 asyncResp->res, diagnosticDataType,
3041 "DiagnosticDataType", "CollectDiagnosticData");
3042 return;
3043 }
3044
3045 OEMDiagnosticType oemDiagType =
3046 getOEMDiagnosticType(oemDiagnosticDataType);
3047
3048 std::string iface;
3049 std::string method;
3050 std::string taskMatchStr;
3051 if (oemDiagType == OEMDiagnosticType::onDemand)
3052 {
3053 iface = crashdumpOnDemandInterface;
3054 method = "GenerateOnDemandLog";
3055 taskMatchStr =
3056 "type='signal',"
3057 "interface='org.freedesktop.DBus.Properties',"
3058 "member='PropertiesChanged',"
3059 "arg0namespace='com.intel.crashdump'";
3060 }
3061 else if (oemDiagType == OEMDiagnosticType::telemetry)
3062 {
3063 iface = crashdumpTelemetryInterface;
3064 method = "GenerateTelemetryLog";
3065 taskMatchStr =
3066 "type='signal',"
3067 "interface='org.freedesktop.DBus.Properties',"
3068 "member='PropertiesChanged',"
3069 "arg0namespace='com.intel.crashdump'";
3070 }
3071 else
3072 {
3073 BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}",
3074 oemDiagnosticDataType);
3075 messages::actionParameterValueFormatError(
3076 asyncResp->res, oemDiagnosticDataType,
3077 "OEMDiagnosticDataType", "CollectDiagnosticData");
3078 return;
3079 }
3080
3081 auto collectCrashdumpCallback =
3082 [asyncResp, payload(task::Payload(req)),
3083 taskMatchStr](const boost::system::error_code& ec,
3084 const std::string&) mutable {
3085 if (ec)
3086 {
3087 if (ec.value() ==
3088 boost::system::errc::operation_not_supported)
3089 {
3090 messages::resourceInStandby(asyncResp->res);
3091 }
3092 else if (ec.value() == boost::system::errc::
3093 device_or_resource_busy)
3094 {
3095 messages::serviceTemporarilyUnavailable(
3096 asyncResp->res, "60");
3097 }
3098 else
3099 {
3100 messages::internalError(asyncResp->res);
3101 }
3102 return;
3103 }
3104 std::shared_ptr<task::TaskData> task =
3105 task::TaskData::createTask(
3106 [](const boost::system::error_code& ec2,
3107 sdbusplus::message_t&,
3108 const std::shared_ptr<task::TaskData>&
3109 taskData) {
3110 if (!ec2)
3111 {
3112 taskData->messages.emplace_back(
3113 messages::taskCompletedOK(
3114 std::to_string(
3115 taskData->index)));
3116 taskData->state = "Completed";
3117 }
3118 return task::completed;
3119 },
3120 taskMatchStr);
3121
3122 task->startTimer(std::chrono::minutes(5));
3123 task->payload.emplace(std::move(payload));
3124 task->populateResp(asyncResp->res);
3125 };
3126
3127 dbus::utility::async_method_call(
3128 asyncResp, std::move(collectCrashdumpCallback),
3129 crashdumpObject, crashdumpPath, iface, method);
3130 });
3131 }
3132
dBusLogServiceActionsClear(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)3133 inline void dBusLogServiceActionsClear(
3134 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
3135 {
3136 BMCWEB_LOG_DEBUG("Do delete all entries.");
3137
3138 // Process response from Logging service.
3139 auto respHandler = [asyncResp](const boost::system::error_code& ec) {
3140 BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done");
3141 if (ec)
3142 {
3143 // TODO Handle for specific error code
3144 BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec);
3145 asyncResp->res.result(
3146 boost::beast::http::status::internal_server_error);
3147 return;
3148 }
3149
3150 messages::success(asyncResp->res);
3151 };
3152
3153 // Make call to Logging service to request Clear Log
3154 dbus::utility::async_method_call(
3155 asyncResp, respHandler, "xyz.openbmc_project.Logging",
3156 "/xyz/openbmc_project/logging",
3157 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3158 }
3159
3160 /**
3161 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3162 */
requestRoutesDBusLogServiceActionsClear(App & app)3163 inline void requestRoutesDBusLogServiceActionsClear(App& app)
3164 {
3165 /**
3166 * Function handles POST method request.
3167 * The Clear Log actions does not require any parameter.The action deletes
3168 * all entries found in the Entries collection for this Log Service.
3169 */
3170
3171 BMCWEB_ROUTE(
3172 app,
3173 "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
3174 .privileges(redfish::privileges::
3175 postLogServiceSubOverComputerSystemLogServiceCollection)
3176 .methods(boost::beast::http::verb::post)(
3177 [&app](const crow::Request& req,
3178 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3179 const std::string& systemName) {
3180 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3181 {
3182 return;
3183 }
3184 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
3185 {
3186 // Option currently returns no systems. TBD
3187 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3188 systemName);
3189 return;
3190 }
3191 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
3192 {
3193 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3194 systemName);
3195 return;
3196 }
3197 dBusLogServiceActionsClear(asyncResp);
3198 });
3199 }
3200
3201 } // namespace redfish
3202