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
178 inline bool
getRedfishLogFiles(std::vector<std::filesystem::path> & redfishLogFiles)179 getRedfishLogFiles(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
203 inline log_entry::OriginatorTypes
mapDbusOriginatorTypeToRedfish(const std::string & originatorType)204 mapDbusOriginatorTypeToRedfish(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
356 inline void
getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)357 getDumpEntryCollection(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
472 inline void
getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & dumpType)473 getDumpEntryById(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 crow::connections::systemBus->async_method_call(
594 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
688 inline void
downloadDumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & dumpType)689 downloadDumpEntry(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 crow::connections::systemBus->async_method_call(
710 std::move(downloadDumpEntryHandler), "xyz.openbmc_project.Dump.Manager",
711 dumpEntryPath, "xyz.openbmc_project.Dump.Entry", "GetFileHandle");
712 }
713
downloadEventLogEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & entryID,const std::string & dumpType)714 inline void downloadEventLogEntry(
715 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
716 const std::string& systemName, const std::string& entryID,
717 const std::string& dumpType)
718 {
719 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
720 {
721 // Option currently returns no systems. TBD
722 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
723 systemName);
724 return;
725 }
726 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
727 {
728 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
729 systemName);
730 return;
731 }
732
733 std::string entryPath =
734 sdbusplus::message::object_path("/xyz/openbmc_project/logging/entry") /
735 entryID;
736
737 auto downloadEventLogEntryHandler =
738 [asyncResp, entryID,
739 dumpType](const boost::system::error_code& ec,
740 const sdbusplus::message::unix_fd& unixfd) {
741 downloadEntryCallback(asyncResp, entryID, dumpType, ec, unixfd);
742 };
743
744 crow::connections::systemBus->async_method_call(
745 std::move(downloadEventLogEntryHandler), "xyz.openbmc_project.Logging",
746 entryPath, "xyz.openbmc_project.Logging.Entry", "GetEntry");
747 }
748
749 inline DumpCreationProgress
mapDbusStatusToDumpProgress(const std::string & status)750 mapDbusStatusToDumpProgress(const std::string& status)
751 {
752 if (status ==
753 "xyz.openbmc_project.Common.Progress.OperationStatus.Failed" ||
754 status == "xyz.openbmc_project.Common.Progress.OperationStatus.Aborted")
755 {
756 return DumpCreationProgress::DUMP_CREATE_FAILED;
757 }
758 if (status ==
759 "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")
760 {
761 return DumpCreationProgress::DUMP_CREATE_SUCCESS;
762 }
763 return DumpCreationProgress::DUMP_CREATE_INPROGRESS;
764 }
765
766 inline DumpCreationProgress
getDumpCompletionStatus(const dbus::utility::DBusPropertiesMap & values)767 getDumpCompletionStatus(const dbus::utility::DBusPropertiesMap& values)
768 {
769 for (const auto& [key, val] : values)
770 {
771 if (key == "Status")
772 {
773 const std::string* value = std::get_if<std::string>(&val);
774 if (value == nullptr)
775 {
776 BMCWEB_LOG_ERROR("Status property value is null");
777 return DumpCreationProgress::DUMP_CREATE_FAILED;
778 }
779 return mapDbusStatusToDumpProgress(*value);
780 }
781 }
782 return DumpCreationProgress::DUMP_CREATE_INPROGRESS;
783 }
784
getDumpEntryPath(const std::string & dumpPath)785 inline std::string getDumpEntryPath(const std::string& dumpPath)
786 {
787 if (dumpPath == "/xyz/openbmc_project/dump/bmc/entry")
788 {
789 return std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/",
790 BMCWEB_REDFISH_MANAGER_URI_NAME);
791 }
792 if (dumpPath == "/xyz/openbmc_project/dump/system/entry")
793 {
794 return std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/",
795 BMCWEB_REDFISH_SYSTEM_URI_NAME);
796 }
797 return "";
798 }
799
createDumpTaskCallback(task::Payload && payload,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const sdbusplus::message::object_path & createdObjPath)800 inline void createDumpTaskCallback(
801 task::Payload&& payload,
802 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
803 const sdbusplus::message::object_path& createdObjPath)
804 {
805 const std::string dumpPath = createdObjPath.parent_path().str;
806 const std::string dumpId = createdObjPath.filename();
807
808 std::string dumpEntryPath = getDumpEntryPath(dumpPath);
809
810 if (dumpEntryPath.empty())
811 {
812 BMCWEB_LOG_ERROR("Invalid dump type received");
813 messages::internalError(asyncResp->res);
814 return;
815 }
816
817 crow::connections::systemBus->async_method_call(
818 [asyncResp, payload = std::move(payload), createdObjPath,
819 dumpEntryPath{std::move(dumpEntryPath)},
820 dumpId](const boost::system::error_code& ec,
821 const std::string& introspectXml) {
822 if (ec)
823 {
824 BMCWEB_LOG_ERROR("Introspect call failed with error: {}",
825 ec.message());
826 messages::internalError(asyncResp->res);
827 return;
828 }
829
830 // Check if the created dump object has implemented Progress
831 // interface to track dump completion. If yes, fetch the "Status"
832 // property of the interface, modify the task state accordingly.
833 // Else, return task completed.
834 tinyxml2::XMLDocument doc;
835
836 doc.Parse(introspectXml.data(), introspectXml.size());
837 tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
838 if (pRoot == nullptr)
839 {
840 BMCWEB_LOG_ERROR("XML document failed to parse");
841 messages::internalError(asyncResp->res);
842 return;
843 }
844 tinyxml2::XMLElement* interfaceNode =
845 pRoot->FirstChildElement("interface");
846
847 bool isProgressIntfPresent = false;
848 while (interfaceNode != nullptr)
849 {
850 const char* thisInterfaceName =
851 interfaceNode->Attribute("name");
852 if (thisInterfaceName != nullptr)
853 {
854 if (thisInterfaceName ==
855 std::string_view("xyz.openbmc_project.Common.Progress"))
856 {
857 interfaceNode =
858 interfaceNode->NextSiblingElement("interface");
859 continue;
860 }
861 isProgressIntfPresent = true;
862 break;
863 }
864 interfaceNode = interfaceNode->NextSiblingElement("interface");
865 }
866
867 std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
868 [createdObjPath, dumpEntryPath, dumpId, isProgressIntfPresent](
869 const boost::system::error_code& ec2,
870 sdbusplus::message_t& msg,
871 const std::shared_ptr<task::TaskData>& taskData) {
872 if (ec2)
873 {
874 BMCWEB_LOG_ERROR("{}: Error in creating dump",
875 createdObjPath.str);
876 taskData->messages.emplace_back(
877 messages::internalError());
878 taskData->state = "Cancelled";
879 return task::completed;
880 }
881
882 if (isProgressIntfPresent)
883 {
884 dbus::utility::DBusPropertiesMap values;
885 std::string prop;
886 msg.read(prop, values);
887
888 DumpCreationProgress dumpStatus =
889 getDumpCompletionStatus(values);
890 if (dumpStatus ==
891 DumpCreationProgress::DUMP_CREATE_FAILED)
892 {
893 BMCWEB_LOG_ERROR("{}: Error in creating dump",
894 createdObjPath.str);
895 taskData->state = "Cancelled";
896 return task::completed;
897 }
898
899 if (dumpStatus ==
900 DumpCreationProgress::DUMP_CREATE_INPROGRESS)
901 {
902 BMCWEB_LOG_DEBUG(
903 "{}: Dump creation task is in progress",
904 createdObjPath.str);
905 return !task::completed;
906 }
907 }
908
909 nlohmann::json retMessage = messages::success();
910 taskData->messages.emplace_back(retMessage);
911
912 boost::urls::url url = boost::urls::format(
913 "/redfish/v1/Managers/{}/LogServices/Dump/Entries/{}",
914 BMCWEB_REDFISH_MANAGER_URI_NAME, dumpId);
915
916 std::string headerLoc = "Location: ";
917 headerLoc += url.buffer();
918
919 taskData->payload->httpHeaders.emplace_back(
920 std::move(headerLoc));
921
922 BMCWEB_LOG_DEBUG("{}: Dump creation task completed",
923 createdObjPath.str);
924 taskData->state = "Completed";
925 return task::completed;
926 },
927 "type='signal',interface='org.freedesktop.DBus.Properties',"
928 "member='PropertiesChanged',path='" +
929 createdObjPath.str + "'");
930
931 // The task timer is set to max time limit within which the
932 // requested dump will be collected.
933 task->startTimer(std::chrono::minutes(6));
934 task->populateResp(asyncResp->res);
935 task->payload.emplace(payload);
936 },
937 "xyz.openbmc_project.Dump.Manager", createdObjPath,
938 "org.freedesktop.DBus.Introspectable", "Introspect");
939 }
940
createDump(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req,const std::string & dumpType)941 inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
942 const crow::Request& req, const std::string& dumpType)
943 {
944 std::string dumpPath = getDumpEntriesPath(dumpType);
945 if (dumpPath.empty())
946 {
947 messages::internalError(asyncResp->res);
948 return;
949 }
950
951 std::optional<std::string> diagnosticDataType;
952 std::optional<std::string> oemDiagnosticDataType;
953
954 if (!redfish::json_util::readJsonAction( //
955 req, asyncResp->res, //
956 "DiagnosticDataType", diagnosticDataType, //
957 "OEMDiagnosticDataType", oemDiagnosticDataType //
958 ))
959 {
960 return;
961 }
962
963 if (dumpType == "System")
964 {
965 if (!oemDiagnosticDataType || !diagnosticDataType)
966 {
967 BMCWEB_LOG_ERROR(
968 "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!");
969 messages::actionParameterMissing(
970 asyncResp->res, "CollectDiagnosticData",
971 "DiagnosticDataType & OEMDiagnosticDataType");
972 return;
973 }
974 if ((*oemDiagnosticDataType != "System") ||
975 (*diagnosticDataType != "OEM"))
976 {
977 BMCWEB_LOG_ERROR("Wrong parameter values passed");
978 messages::internalError(asyncResp->res);
979 return;
980 }
981 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump/",
982 BMCWEB_REDFISH_SYSTEM_URI_NAME);
983 }
984 else if (dumpType == "BMC")
985 {
986 if (!diagnosticDataType)
987 {
988 BMCWEB_LOG_ERROR(
989 "CreateDump action parameter 'DiagnosticDataType' not found!");
990 messages::actionParameterMissing(
991 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
992 return;
993 }
994 if (*diagnosticDataType != "Manager")
995 {
996 BMCWEB_LOG_ERROR(
997 "Wrong parameter value passed for 'DiagnosticDataType'");
998 messages::internalError(asyncResp->res);
999 return;
1000 }
1001 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump/",
1002 BMCWEB_REDFISH_MANAGER_URI_NAME);
1003 }
1004 else
1005 {
1006 BMCWEB_LOG_ERROR("CreateDump failed. Unknown dump type");
1007 messages::internalError(asyncResp->res);
1008 return;
1009 }
1010
1011 std::vector<std::pair<std::string, std::variant<std::string, uint64_t>>>
1012 createDumpParamVec;
1013
1014 if (req.session != nullptr)
1015 {
1016 createDumpParamVec.emplace_back(
1017 "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorId",
1018 req.session->clientIp);
1019 createDumpParamVec.emplace_back(
1020 "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorType",
1021 "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client");
1022 }
1023
1024 crow::connections::systemBus->async_method_call(
1025 [asyncResp, payload(task::Payload(req)),
1026 dumpPath](const boost::system::error_code& ec,
1027 const sdbusplus::message_t& msg,
1028 const sdbusplus::message::object_path& objPath) mutable {
1029 if (ec)
1030 {
1031 BMCWEB_LOG_ERROR("CreateDump resp_handler got error {}", ec);
1032 const sd_bus_error* dbusError = msg.get_error();
1033 if (dbusError == nullptr)
1034 {
1035 messages::internalError(asyncResp->res);
1036 return;
1037 }
1038
1039 BMCWEB_LOG_ERROR("CreateDump DBus error: {} and error msg: {}",
1040 dbusError->name, dbusError->message);
1041 if (std::string_view(
1042 "xyz.openbmc_project.Common.Error.NotAllowed") ==
1043 dbusError->name)
1044 {
1045 messages::resourceInStandby(asyncResp->res);
1046 return;
1047 }
1048 if (std::string_view(
1049 "xyz.openbmc_project.Dump.Create.Error.Disabled") ==
1050 dbusError->name)
1051 {
1052 messages::serviceDisabled(asyncResp->res, dumpPath);
1053 return;
1054 }
1055 if (std::string_view(
1056 "xyz.openbmc_project.Common.Error.Unavailable") ==
1057 dbusError->name)
1058 {
1059 messages::resourceInUse(asyncResp->res);
1060 return;
1061 }
1062 // Other Dbus errors such as:
1063 // xyz.openbmc_project.Common.Error.InvalidArgument &
1064 // org.freedesktop.DBus.Error.InvalidArgs are all related to
1065 // the dbus call that is made here in the bmcweb
1066 // implementation and has nothing to do with the client's
1067 // input in the request. Hence, returning internal error
1068 // back to the client.
1069 messages::internalError(asyncResp->res);
1070 return;
1071 }
1072 BMCWEB_LOG_DEBUG("Dump Created. Path: {}", objPath.str);
1073 createDumpTaskCallback(std::move(payload), asyncResp, objPath);
1074 },
1075 "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType),
1076 "xyz.openbmc_project.Dump.Create", "CreateDump", createDumpParamVec);
1077 }
1078
clearDump(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)1079 inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1080 const std::string& dumpType)
1081 {
1082 crow::connections::systemBus->async_method_call(
1083 [asyncResp](const boost::system::error_code& ec) {
1084 if (ec)
1085 {
1086 BMCWEB_LOG_ERROR("clearDump resp_handler got error {}", ec);
1087 messages::internalError(asyncResp->res);
1088 return;
1089 }
1090 },
1091 "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType),
1092 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1093 }
1094
parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap & params,std::string & filename,std::string & timestamp,std::string & logfile)1095 inline void parseCrashdumpParameters(
1096 const dbus::utility::DBusPropertiesMap& params, std::string& filename,
1097 std::string& timestamp, std::string& logfile)
1098 {
1099 const std::string* filenamePtr = nullptr;
1100 const std::string* timestampPtr = nullptr;
1101 const std::string* logfilePtr = nullptr;
1102
1103 const bool success = sdbusplus::unpackPropertiesNoThrow(
1104 dbus_utils::UnpackErrorPrinter(), params, "Timestamp", timestampPtr,
1105 "Filename", filenamePtr, "Log", logfilePtr);
1106
1107 if (!success)
1108 {
1109 return;
1110 }
1111
1112 if (filenamePtr != nullptr)
1113 {
1114 filename = *filenamePtr;
1115 }
1116
1117 if (timestampPtr != nullptr)
1118 {
1119 timestamp = *timestampPtr;
1120 }
1121
1122 if (logfilePtr != nullptr)
1123 {
1124 logfile = *logfilePtr;
1125 }
1126 }
1127
requestRoutesSystemLogServiceCollection(App & app)1128 inline void requestRoutesSystemLogServiceCollection(App& app)
1129 {
1130 /**
1131 * Functions triggers appropriate requests on DBus
1132 */
1133 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/")
1134 .privileges(redfish::privileges::getLogServiceCollection)
1135 .methods(
1136 boost::beast::http::verb::
1137 get)([&app](const crow::Request& req,
1138 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1139 const std::string& systemName) {
1140 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1141 {
1142 return;
1143 }
1144 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1145 {
1146 // Option currently returns no systems. TBD
1147 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1148 systemName);
1149 return;
1150 }
1151 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1152 {
1153 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1154 systemName);
1155 return;
1156 }
1157
1158 // Collections don't include the static data added by SubRoute
1159 // because it has a duplicate entry for members
1160 asyncResp->res.jsonValue["@odata.type"] =
1161 "#LogServiceCollection.LogServiceCollection";
1162 asyncResp->res.jsonValue["@odata.id"] =
1163 std::format("/redfish/v1/Systems/{}/LogServices",
1164 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1165 asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
1166 asyncResp->res.jsonValue["Description"] =
1167 "Collection of LogServices for this Computer System";
1168 nlohmann::json& logServiceArray =
1169 asyncResp->res.jsonValue["Members"];
1170 logServiceArray = nlohmann::json::array();
1171 nlohmann::json::object_t eventLog;
1172 eventLog["@odata.id"] =
1173 std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
1174 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1175 logServiceArray.emplace_back(std::move(eventLog));
1176 if constexpr (BMCWEB_REDFISH_DUMP_LOG)
1177 {
1178 nlohmann::json::object_t dumpLog;
1179 dumpLog["@odata.id"] =
1180 std::format("/redfish/v1/Systems/{}/LogServices/Dump",
1181 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1182 logServiceArray.emplace_back(std::move(dumpLog));
1183 }
1184
1185 if constexpr (BMCWEB_REDFISH_CPU_LOG)
1186 {
1187 nlohmann::json::object_t crashdump;
1188 crashdump["@odata.id"] =
1189 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
1190 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1191 logServiceArray.emplace_back(std::move(crashdump));
1192 }
1193
1194 if constexpr (BMCWEB_REDFISH_HOST_LOGGER)
1195 {
1196 nlohmann::json::object_t hostlogger;
1197 hostlogger["@odata.id"] =
1198 std::format("/redfish/v1/Systems/{}/LogServices/HostLogger",
1199 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1200 logServiceArray.emplace_back(std::move(hostlogger));
1201 }
1202 asyncResp->res.jsonValue["Members@odata.count"] =
1203 logServiceArray.size();
1204
1205 constexpr std::array<std::string_view, 1> interfaces = {
1206 "xyz.openbmc_project.State.Boot.PostCode"};
1207 dbus::utility::getSubTreePaths(
1208 "/", 0, interfaces,
1209 [asyncResp](const boost::system::error_code& ec,
1210 const dbus::utility::MapperGetSubTreePathsResponse&
1211 subtreePath) {
1212 if (ec)
1213 {
1214 BMCWEB_LOG_ERROR("{}", ec);
1215 return;
1216 }
1217
1218 for (const auto& pathStr : subtreePath)
1219 {
1220 if (pathStr.find("PostCode") != std::string::npos)
1221 {
1222 nlohmann::json& logServiceArrayLocal =
1223 asyncResp->res.jsonValue["Members"];
1224 nlohmann::json::object_t member;
1225 member["@odata.id"] = std::format(
1226 "/redfish/v1/Systems/{}/LogServices/PostCodes",
1227 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1228
1229 logServiceArrayLocal.emplace_back(
1230 std::move(member));
1231
1232 asyncResp->res.jsonValue["Members@odata.count"] =
1233 logServiceArrayLocal.size();
1234 return;
1235 }
1236 }
1237 });
1238 });
1239 }
1240
requestRoutesEventLogService(App & app)1241 inline void requestRoutesEventLogService(App& app)
1242 {
1243 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/")
1244 .privileges(redfish::privileges::getLogService)
1245 .methods(
1246 boost::beast::http::verb::
1247 get)([&app](const crow::Request& req,
1248 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1249 const std::string& systemName) {
1250 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1251 {
1252 return;
1253 }
1254 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1255 {
1256 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1257 systemName);
1258 return;
1259 }
1260 asyncResp->res.jsonValue["@odata.id"] =
1261 std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
1262 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1263 asyncResp->res.jsonValue["@odata.type"] =
1264 "#LogService.v1_2_0.LogService";
1265 asyncResp->res.jsonValue["Name"] = "Event Log Service";
1266 asyncResp->res.jsonValue["Description"] =
1267 "System Event Log Service";
1268 asyncResp->res.jsonValue["Id"] = "EventLog";
1269 asyncResp->res.jsonValue["OverWritePolicy"] =
1270 log_service::OverWritePolicy::WrapsWhenFull;
1271
1272 std::pair<std::string, std::string> redfishDateTimeOffset =
1273 redfish::time_utils::getDateTimeOffsetNow();
1274
1275 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1276 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1277 redfishDateTimeOffset.second;
1278
1279 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
1280 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1281 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1282 asyncResp->res
1283 .jsonValue["Actions"]["#LogService.ClearLog"]["target"]
1284
1285 = std::format(
1286 "/redfish/v1/Systems/{}/LogServices/EventLog/Actions/LogService.ClearLog",
1287 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1288 });
1289 }
1290
handleSystemsLogServicesEventLogActionsClearPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1291 inline void handleSystemsLogServicesEventLogActionsClearPost(
1292 App& app, const crow::Request& req,
1293 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1294 const std::string& systemName)
1295 {
1296 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1297 {
1298 return;
1299 }
1300 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1301 {
1302 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1303 systemName);
1304 return;
1305 }
1306
1307 // Clear the EventLog by deleting the log files
1308 std::vector<std::filesystem::path> redfishLogFiles;
1309 if (getRedfishLogFiles(redfishLogFiles))
1310 {
1311 for (const std::filesystem::path& file : redfishLogFiles)
1312 {
1313 std::error_code ec;
1314 std::filesystem::remove(file, ec);
1315 }
1316 }
1317
1318 // Reload rsyslog so it knows to start new log files
1319 crow::connections::systemBus->async_method_call(
1320 [asyncResp](const boost::system::error_code& ec) {
1321 if (ec)
1322 {
1323 BMCWEB_LOG_ERROR("Failed to reload rsyslog: {}", ec);
1324 messages::internalError(asyncResp->res);
1325 return;
1326 }
1327
1328 messages::success(asyncResp->res);
1329 },
1330 "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1331 "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1332 "replace");
1333 }
1334
requestRoutesJournalEventLogClear(App & app)1335 inline void requestRoutesJournalEventLogClear(App& app)
1336 {
1337 BMCWEB_ROUTE(
1338 app,
1339 "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
1340 .privileges({{"ConfigureComponents"}})
1341 .methods(boost::beast::http::verb::post)(std::bind_front(
1342 handleSystemsLogServicesEventLogActionsClearPost, std::ref(app)));
1343 }
1344
1345 enum class LogParseError
1346 {
1347 success,
1348 parseFailed,
1349 messageIdNotInRegistry,
1350 };
1351
fillEventLogEntryJson(const std::string & logEntryID,const std::string & logEntry,nlohmann::json::object_t & logEntryJson)1352 static LogParseError fillEventLogEntryJson(
1353 const std::string& logEntryID, const std::string& logEntry,
1354 nlohmann::json::object_t& logEntryJson)
1355 {
1356 // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
1357 // First get the Timestamp
1358 size_t space = logEntry.find_first_of(' ');
1359 if (space == std::string::npos)
1360 {
1361 return LogParseError::parseFailed;
1362 }
1363 std::string timestamp = logEntry.substr(0, space);
1364 // Then get the log contents
1365 size_t entryStart = logEntry.find_first_not_of(' ', space);
1366 if (entryStart == std::string::npos)
1367 {
1368 return LogParseError::parseFailed;
1369 }
1370 std::string_view entry(logEntry);
1371 entry.remove_prefix(entryStart);
1372 // Use split to separate the entry into its fields
1373 std::vector<std::string> logEntryFields;
1374 bmcweb::split(logEntryFields, entry, ',');
1375 // We need at least a MessageId to be valid
1376 auto logEntryIter = logEntryFields.begin();
1377 if (logEntryIter == logEntryFields.end())
1378 {
1379 return LogParseError::parseFailed;
1380 }
1381 std::string& messageID = *logEntryIter;
1382 // Get the Message from the MessageRegistry
1383 const registries::Message* message = registries::getMessage(messageID);
1384
1385 logEntryIter++;
1386 if (message == nullptr)
1387 {
1388 BMCWEB_LOG_WARNING("Log entry not found in registry: {}", logEntry);
1389 return LogParseError::messageIdNotInRegistry;
1390 }
1391
1392 std::vector<std::string_view> messageArgs(logEntryIter,
1393 logEntryFields.end());
1394 messageArgs.resize(message->numberOfArgs);
1395
1396 std::string msg =
1397 redfish::registries::fillMessageArgs(messageArgs, message->message);
1398 if (msg.empty())
1399 {
1400 return LogParseError::parseFailed;
1401 }
1402
1403 // Get the Created time from the timestamp. The log timestamp is in RFC3339
1404 // format which matches the Redfish format except for the fractional seconds
1405 // between the '.' and the '+', so just remove them.
1406 std::size_t dot = timestamp.find_first_of('.');
1407 std::size_t plus = timestamp.find_first_of('+');
1408 if (dot != std::string::npos && plus != std::string::npos)
1409 {
1410 timestamp.erase(dot, plus - dot);
1411 }
1412
1413 // Fill in the log entry with the gathered data
1414 logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1415 logEntryJson["@odata.id"] = boost::urls::format(
1416 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}",
1417 BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID);
1418 logEntryJson["Name"] = "System Event Log Entry";
1419 logEntryJson["Id"] = logEntryID;
1420 logEntryJson["Message"] = std::move(msg);
1421 logEntryJson["MessageId"] = std::move(messageID);
1422 logEntryJson["MessageArgs"] = messageArgs;
1423 logEntryJson["EntryType"] = "Event";
1424 logEntryJson["Severity"] = message->messageSeverity;
1425 logEntryJson["Created"] = std::move(timestamp);
1426 return LogParseError::success;
1427 }
1428
fillEventLogLogEntryFromPropertyMap(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & resp,nlohmann::json & objectToFillOut)1429 inline void fillEventLogLogEntryFromPropertyMap(
1430 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1431 const dbus::utility::DBusPropertiesMap& resp,
1432 nlohmann::json& objectToFillOut)
1433 {
1434 std::optional<DbusEventLogEntry> optEntry =
1435 fillDbusEventLogEntryFromPropertyMap(resp);
1436
1437 if (!optEntry.has_value())
1438 {
1439 messages::internalError(asyncResp->res);
1440 return;
1441 }
1442 DbusEventLogEntry entry = optEntry.value();
1443
1444 objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1445 objectToFillOut["@odata.id"] = boost::urls::format(
1446 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}",
1447 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id));
1448 objectToFillOut["Name"] = "System Event Log Entry";
1449 objectToFillOut["Id"] = std::to_string(entry.Id);
1450 objectToFillOut["Message"] = entry.Message;
1451 objectToFillOut["Resolved"] = entry.Resolved;
1452 std::optional<bool> notifyAction =
1453 getProviderNotifyAction(entry.ServiceProviderNotify);
1454 if (notifyAction)
1455 {
1456 objectToFillOut["ServiceProviderNotified"] = *notifyAction;
1457 }
1458 if ((entry.Resolution != nullptr) && !entry.Resolution->empty())
1459 {
1460 objectToFillOut["Resolution"] = *entry.Resolution;
1461 }
1462 objectToFillOut["EntryType"] = "Event";
1463 objectToFillOut["Severity"] =
1464 translateSeverityDbusToRedfish(entry.Severity);
1465 objectToFillOut["Created"] =
1466 redfish::time_utils::getDateTimeUintMs(entry.Timestamp);
1467 objectToFillOut["Modified"] =
1468 redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp);
1469 if (entry.Path != nullptr)
1470 {
1471 objectToFillOut["AdditionalDataURI"] = boost::urls::format(
1472 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}/attachment",
1473 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id));
1474 }
1475 }
1476
afterLogEntriesGetManagedObjects(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::ManagedObjectType & resp)1477 inline void afterLogEntriesGetManagedObjects(
1478 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1479 const boost::system::error_code& ec,
1480 const dbus::utility::ManagedObjectType& resp)
1481 {
1482 if (ec)
1483 {
1484 // TODO Handle for specific error code
1485 BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}",
1486 ec);
1487 messages::internalError(asyncResp->res);
1488 return;
1489 }
1490 nlohmann::json::array_t entriesArray;
1491 for (const auto& objectPath : resp)
1492 {
1493 dbus::utility::DBusPropertiesMap propsFlattened;
1494 auto isEntry =
1495 std::ranges::find_if(objectPath.second, [](const auto& object) {
1496 return object.first == "xyz.openbmc_project.Logging.Entry";
1497 });
1498 if (isEntry == objectPath.second.end())
1499 {
1500 continue;
1501 }
1502 for (const auto& interfaceMap : objectPath.second)
1503 {
1504 for (const auto& propertyMap : interfaceMap.second)
1505 {
1506 propsFlattened.emplace_back(propertyMap.first,
1507 propertyMap.second);
1508 }
1509 }
1510 fillEventLogLogEntryFromPropertyMap(asyncResp, propsFlattened,
1511 entriesArray.emplace_back());
1512 }
1513
1514 std::ranges::sort(entriesArray, [](const nlohmann::json& left,
1515 const nlohmann::json& right) {
1516 return (left["Id"] <= right["Id"]);
1517 });
1518 asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
1519 asyncResp->res.jsonValue["Members"] = std::move(entriesArray);
1520 }
1521
handleSystemsLogServiceEventLogLogEntryCollection(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1522 inline void handleSystemsLogServiceEventLogLogEntryCollection(
1523 App& app, const crow::Request& req,
1524 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1525 const std::string& systemName)
1526 {
1527 query_param::QueryCapabilities capabilities = {
1528 .canDelegateTop = true,
1529 .canDelegateSkip = true,
1530 };
1531 query_param::Query delegatedQuery;
1532 if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
1533 delegatedQuery, capabilities))
1534 {
1535 return;
1536 }
1537 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1538 {
1539 // Option currently returns no systems. TBD
1540 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1541 systemName);
1542 return;
1543 }
1544 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1545 {
1546 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1547 systemName);
1548 return;
1549 }
1550
1551 size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
1552 size_t skip = delegatedQuery.skip.value_or(0);
1553
1554 // Collections don't include the static data added by SubRoute
1555 // because it has a duplicate entry for members
1556 asyncResp->res.jsonValue["@odata.type"] =
1557 "#LogEntryCollection.LogEntryCollection";
1558 asyncResp->res.jsonValue["@odata.id"] =
1559 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1560 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1561 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1562 asyncResp->res.jsonValue["Description"] =
1563 "Collection of System Event Log Entries";
1564
1565 nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1566 logEntryArray = nlohmann::json::array();
1567 // Go through the log files and create a unique ID for each
1568 // entry
1569 std::vector<std::filesystem::path> redfishLogFiles;
1570 getRedfishLogFiles(redfishLogFiles);
1571 uint64_t entryCount = 0;
1572 std::string logEntry;
1573
1574 // Oldest logs are in the last file, so start there and loop
1575 // backwards
1576 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
1577 {
1578 std::ifstream logStream(*it);
1579 if (!logStream.is_open())
1580 {
1581 continue;
1582 }
1583
1584 // Reset the unique ID on the first entry
1585 bool firstEntry = true;
1586 while (std::getline(logStream, logEntry))
1587 {
1588 std::string idStr;
1589 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1590 {
1591 continue;
1592 }
1593 firstEntry = false;
1594
1595 nlohmann::json::object_t bmcLogEntry;
1596 LogParseError status =
1597 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1598 if (status == LogParseError::messageIdNotInRegistry)
1599 {
1600 continue;
1601 }
1602 if (status != LogParseError::success)
1603 {
1604 messages::internalError(asyncResp->res);
1605 return;
1606 }
1607
1608 entryCount++;
1609 // Handle paging using skip (number of entries to skip from the
1610 // start) and top (number of entries to display)
1611 if (entryCount <= skip || entryCount > skip + top)
1612 {
1613 continue;
1614 }
1615
1616 logEntryArray.emplace_back(std::move(bmcLogEntry));
1617 }
1618 }
1619 asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1620 if (skip + top < entryCount)
1621 {
1622 asyncResp->res.jsonValue["Members@odata.nextLink"] =
1623 boost::urls::format(
1624 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries?$skip={}",
1625 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(skip + top));
1626 }
1627 }
1628
requestRoutesJournalEventLogEntryCollection(App & app)1629 inline void requestRoutesJournalEventLogEntryCollection(App& app)
1630 {
1631 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1632 .privileges(redfish::privileges::getLogEntryCollection)
1633 .methods(boost::beast::http::verb::get)(std::bind_front(
1634 handleSystemsLogServiceEventLogLogEntryCollection, std::ref(app)));
1635 }
1636
handleSystemsLogServiceEventLogEntriesGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & param)1637 inline void handleSystemsLogServiceEventLogEntriesGet(
1638 App& app, const crow::Request& req,
1639 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1640 const std::string& systemName, const std::string& param)
1641 {
1642 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1643 {
1644 return;
1645 }
1646 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1647 {
1648 // Option currently returns no systems. TBD
1649 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1650 systemName);
1651 return;
1652 }
1653
1654 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1655 {
1656 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1657 systemName);
1658 return;
1659 }
1660
1661 const std::string& targetID = param;
1662
1663 // Go through the log files and check the unique ID for each
1664 // entry to find the target entry
1665 std::vector<std::filesystem::path> redfishLogFiles;
1666 getRedfishLogFiles(redfishLogFiles);
1667 std::string logEntry;
1668
1669 // Oldest logs are in the last file, so start there and loop
1670 // backwards
1671 for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
1672 {
1673 std::ifstream logStream(*it);
1674 if (!logStream.is_open())
1675 {
1676 continue;
1677 }
1678
1679 // Reset the unique ID on the first entry
1680 bool firstEntry = true;
1681 while (std::getline(logStream, logEntry))
1682 {
1683 std::string idStr;
1684 if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1685 {
1686 continue;
1687 }
1688 firstEntry = false;
1689
1690 if (idStr == targetID)
1691 {
1692 nlohmann::json::object_t bmcLogEntry;
1693 LogParseError status =
1694 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1695 if (status != LogParseError::success)
1696 {
1697 messages::internalError(asyncResp->res);
1698 return;
1699 }
1700 asyncResp->res.jsonValue.update(bmcLogEntry);
1701 return;
1702 }
1703 }
1704 }
1705 // Requested ID was not found
1706 messages::resourceNotFound(asyncResp->res, "LogEntry", targetID);
1707 }
1708
requestRoutesJournalEventLogEntry(App & app)1709 inline void requestRoutesJournalEventLogEntry(App& app)
1710 {
1711 BMCWEB_ROUTE(
1712 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1713 .privileges(redfish::privileges::getLogEntry)
1714 .methods(boost::beast::http::verb::get)(std::bind_front(
1715 handleSystemsLogServiceEventLogEntriesGet, std::ref(app)));
1716 }
1717
dBusEventLogEntryCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1718 inline void dBusEventLogEntryCollection(
1719 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1720 {
1721 // Collections don't include the static data added by SubRoute
1722 // because it has a duplicate entry for members
1723 asyncResp->res.jsonValue["@odata.type"] =
1724 "#LogEntryCollection.LogEntryCollection";
1725 asyncResp->res.jsonValue["@odata.id"] =
1726 std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1727 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1728 asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1729 asyncResp->res.jsonValue["Description"] =
1730 "Collection of System Event Log Entries";
1731
1732 // DBus implementation of EventLog/Entries
1733 // Make call to Logging Service to find all log entry objects
1734 sdbusplus::message::object_path path("/xyz/openbmc_project/logging");
1735 dbus::utility::getManagedObjects(
1736 "xyz.openbmc_project.Logging", path,
1737 [asyncResp](const boost::system::error_code& ec,
1738 const dbus::utility::ManagedObjectType& resp) {
1739 afterLogEntriesGetManagedObjects(asyncResp, ec, resp);
1740 });
1741 }
1742
requestRoutesDBusEventLogEntryCollection(App & app)1743 inline void requestRoutesDBusEventLogEntryCollection(App& app)
1744 {
1745 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1746 .privileges(redfish::privileges::getLogEntryCollection)
1747 .methods(boost::beast::http::verb::get)(
1748 [&app](const crow::Request& req,
1749 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1750 const std::string& systemName) {
1751 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1752 {
1753 return;
1754 }
1755 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1756 {
1757 // Option currently returns no systems. TBD
1758 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1759 systemName);
1760 return;
1761 }
1762 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1763 {
1764 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1765 systemName);
1766 return;
1767 }
1768 dBusEventLogEntryCollection(asyncResp);
1769 });
1770 }
1771
dBusEventLogEntryGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string entryID)1772 inline void dBusEventLogEntryGet(
1773 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
1774 {
1775 dbus::utility::escapePathForDbus(entryID);
1776
1777 // DBus implementation of EventLog/Entries
1778 // Make call to Logging Service to find all log entry objects
1779 dbus::utility::getAllProperties(
1780 "xyz.openbmc_project.Logging",
1781 "/xyz/openbmc_project/logging/entry/" + entryID, "",
1782 [asyncResp, entryID](const boost::system::error_code& ec,
1783 const dbus::utility::DBusPropertiesMap& resp) {
1784 if (ec.value() == EBADR)
1785 {
1786 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1787 entryID);
1788 return;
1789 }
1790 if (ec)
1791 {
1792 BMCWEB_LOG_ERROR(
1793 "EventLogEntry (DBus) resp_handler got error {}", ec);
1794 messages::internalError(asyncResp->res);
1795 return;
1796 }
1797
1798 fillEventLogLogEntryFromPropertyMap(asyncResp, resp,
1799 asyncResp->res.jsonValue);
1800 });
1801 }
1802
1803 inline void
dBusEventLogEntryPatch(const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryId)1804 dBusEventLogEntryPatch(const crow::Request& req,
1805 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1806 const std::string& entryId)
1807 {
1808 std::optional<bool> resolved;
1809
1810 if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved))
1811 {
1812 return;
1813 }
1814 BMCWEB_LOG_DEBUG("Set Resolved");
1815
1816 setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging",
1817 "/xyz/openbmc_project/logging/entry/" + entryId,
1818 "xyz.openbmc_project.Logging.Entry", "Resolved",
1819 resolved.value_or(false));
1820 }
1821
dBusEventLogEntryDelete(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string entryID)1822 inline void dBusEventLogEntryDelete(
1823 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
1824 {
1825 BMCWEB_LOG_DEBUG("Do delete single event entries.");
1826
1827 dbus::utility::escapePathForDbus(entryID);
1828
1829 // Process response from Logging service.
1830 auto respHandler = [asyncResp,
1831 entryID](const boost::system::error_code& ec) {
1832 BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done");
1833 if (ec)
1834 {
1835 if (ec.value() == EBADR)
1836 {
1837 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
1838 return;
1839 }
1840 // TODO Handle for specific error code
1841 BMCWEB_LOG_ERROR(
1842 "EventLogEntry (DBus) doDelete respHandler got error {}", ec);
1843 asyncResp->res.result(
1844 boost::beast::http::status::internal_server_error);
1845 return;
1846 }
1847
1848 asyncResp->res.result(boost::beast::http::status::ok);
1849 };
1850
1851 // Make call to Logging service to request Delete Log
1852 crow::connections::systemBus->async_method_call(
1853 respHandler, "xyz.openbmc_project.Logging",
1854 "/xyz/openbmc_project/logging/entry/" + entryID,
1855 "xyz.openbmc_project.Object.Delete", "Delete");
1856 }
1857
requestRoutesDBusEventLogEntry(App & app)1858 inline void requestRoutesDBusEventLogEntry(App& app)
1859 {
1860 BMCWEB_ROUTE(
1861 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1862 .privileges(redfish::privileges::getLogEntry)
1863 .methods(boost::beast::http::verb::get)(
1864 [&app](const crow::Request& req,
1865 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1866 const std::string& systemName, const std::string& entryId) {
1867 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1868 {
1869 return;
1870 }
1871 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1872 {
1873 // Option currently returns no systems. TBD
1874 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1875 systemName);
1876 return;
1877 }
1878 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1879 {
1880 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1881 systemName);
1882 return;
1883 }
1884
1885 dBusEventLogEntryGet(asyncResp, entryId);
1886 });
1887
1888 BMCWEB_ROUTE(
1889 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1890 .privileges(redfish::privileges::patchLogEntry)
1891 .methods(boost::beast::http::verb::patch)(
1892 [&app](const crow::Request& req,
1893 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1894 const std::string& systemName, const std::string& entryId) {
1895 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1896 {
1897 return;
1898 }
1899 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1900 {
1901 // Option currently returns no systems. TBD
1902 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1903 systemName);
1904 return;
1905 }
1906 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1907 {
1908 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1909 systemName);
1910 return;
1911 }
1912
1913 dBusEventLogEntryPatch(req, asyncResp, entryId);
1914 });
1915
1916 BMCWEB_ROUTE(
1917 app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1918 .privileges(redfish::privileges::deleteLogEntry)
1919
1920 .methods(boost::beast::http::verb::delete_)(
1921 [&app](const crow::Request& req,
1922 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1923 const std::string& systemName, const std::string& param) {
1924 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1925 {
1926 return;
1927 }
1928 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1929 {
1930 // Option currently returns no systems. TBD
1931 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1932 systemName);
1933 return;
1934 }
1935 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1936 {
1937 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1938 systemName);
1939 return;
1940 }
1941 dBusEventLogEntryDelete(asyncResp, param);
1942 });
1943 }
1944
handleBMCLogServicesCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1945 inline void handleBMCLogServicesCollectionGet(
1946 crow::App& app, const crow::Request& req,
1947 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1948 const std::string& managerId)
1949 {
1950 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1951 {
1952 return;
1953 }
1954
1955 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1956 {
1957 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1958 return;
1959 }
1960
1961 // Collections don't include the static data added by SubRoute
1962 // because it has a duplicate entry for members
1963 asyncResp->res.jsonValue["@odata.type"] =
1964 "#LogServiceCollection.LogServiceCollection";
1965 asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1966 "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME);
1967 asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1968 asyncResp->res.jsonValue["Description"] =
1969 "Collection of LogServices for this Manager";
1970 nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
1971 logServiceArray = nlohmann::json::array();
1972
1973 if constexpr (BMCWEB_REDFISH_BMC_JOURNAL)
1974 {
1975 nlohmann::json::object_t journal;
1976 journal["@odata.id"] =
1977 boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal",
1978 BMCWEB_REDFISH_MANAGER_URI_NAME);
1979 logServiceArray.emplace_back(std::move(journal));
1980 }
1981
1982 asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
1983
1984 if constexpr (BMCWEB_REDFISH_DUMP_LOG)
1985 {
1986 constexpr std::array<std::string_view, 1> interfaces = {
1987 "xyz.openbmc_project.Collection.DeleteAll"};
1988 dbus::utility::getSubTreePaths(
1989 "/xyz/openbmc_project/dump", 0, interfaces,
1990 [asyncResp](const boost::system::error_code& ec,
1991 const dbus::utility::MapperGetSubTreePathsResponse&
1992 subTreePaths) {
1993 if (ec)
1994 {
1995 BMCWEB_LOG_ERROR(
1996 "handleBMCLogServicesCollectionGet respHandler got error {}",
1997 ec);
1998 // Assume that getting an error simply means there are no
1999 // dump LogServices. Return without adding any error
2000 // response.
2001 return;
2002 }
2003
2004 nlohmann::json& logServiceArrayLocal =
2005 asyncResp->res.jsonValue["Members"];
2006
2007 for (const std::string& path : subTreePaths)
2008 {
2009 if (path == "/xyz/openbmc_project/dump/bmc")
2010 {
2011 nlohmann::json::object_t member;
2012 member["@odata.id"] = boost::urls::format(
2013 "/redfish/v1/Managers/{}/LogServices/Dump",
2014 BMCWEB_REDFISH_MANAGER_URI_NAME);
2015 logServiceArrayLocal.emplace_back(std::move(member));
2016 }
2017 else if (path == "/xyz/openbmc_project/dump/faultlog")
2018 {
2019 nlohmann::json::object_t member;
2020 member["@odata.id"] = boost::urls::format(
2021 "/redfish/v1/Managers/{}/LogServices/FaultLog",
2022 BMCWEB_REDFISH_MANAGER_URI_NAME);
2023 logServiceArrayLocal.emplace_back(std::move(member));
2024 }
2025 }
2026
2027 asyncResp->res.jsonValue["Members@odata.count"] =
2028 logServiceArrayLocal.size();
2029 });
2030 }
2031 }
2032
requestRoutesBMCLogServiceCollection(App & app)2033 inline void requestRoutesBMCLogServiceCollection(App& app)
2034 {
2035 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/")
2036 .privileges(redfish::privileges::getLogServiceCollection)
2037 .methods(boost::beast::http::verb::get)(
2038 std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app)));
2039 }
2040
2041 inline void
getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)2042 getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2043 const std::string& dumpType)
2044 {
2045 std::string dumpPath;
2046 log_service::OverWritePolicy overWritePolicy =
2047 log_service::OverWritePolicy::Invalid;
2048 bool collectDiagnosticDataSupported = false;
2049
2050 if (dumpType == "BMC")
2051 {
2052 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump",
2053 BMCWEB_REDFISH_MANAGER_URI_NAME);
2054 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
2055 collectDiagnosticDataSupported = true;
2056 }
2057 else if (dumpType == "FaultLog")
2058 {
2059 dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog",
2060 BMCWEB_REDFISH_MANAGER_URI_NAME);
2061 overWritePolicy = log_service::OverWritePolicy::Unknown;
2062 collectDiagnosticDataSupported = false;
2063 }
2064 else if (dumpType == "System")
2065 {
2066 dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump",
2067 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2068 overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
2069 collectDiagnosticDataSupported = true;
2070 }
2071 else
2072 {
2073 BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}",
2074 dumpType);
2075 messages::internalError(asyncResp->res);
2076 return;
2077 }
2078
2079 asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2080 asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2081 asyncResp->res.jsonValue["Name"] = "Dump LogService";
2082 asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2083 asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2084 asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy;
2085
2086 std::pair<std::string, std::string> redfishDateTimeOffset =
2087 redfish::time_utils::getDateTimeOffsetNow();
2088 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2089 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2090 redfishDateTimeOffset.second;
2091
2092 asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2093
2094 if (collectDiagnosticDataSupported)
2095 {
2096 asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2097 ["target"] =
2098 dumpPath + "/Actions/LogService.CollectDiagnosticData";
2099 }
2100
2101 constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface};
2102 dbus::utility::getSubTreePaths(
2103 "/xyz/openbmc_project/dump", 0, interfaces,
2104 [asyncResp, dumpType, dumpPath](
2105 const boost::system::error_code& ec,
2106 const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2107 if (ec)
2108 {
2109 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}",
2110 ec);
2111 // Assume that getting an error simply means there are no dump
2112 // LogServices. Return without adding any error response.
2113 return;
2114 }
2115 std::string dbusDumpPath = getDumpPath(dumpType);
2116 for (const std::string& path : subTreePaths)
2117 {
2118 if (path == dbusDumpPath)
2119 {
2120 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
2121 ["target"] =
2122 dumpPath + "/Actions/LogService.ClearLog";
2123 break;
2124 }
2125 }
2126 });
2127 }
2128
handleLogServicesDumpServiceGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2129 inline void handleLogServicesDumpServiceGet(
2130 crow::App& app, const std::string& dumpType, const crow::Request& req,
2131 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2132 const std::string& managerId)
2133 {
2134 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2135 {
2136 return;
2137 }
2138
2139 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2140 {
2141 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2142 return;
2143 }
2144
2145 getDumpServiceInfo(asyncResp, dumpType);
2146 }
2147
handleLogServicesDumpServiceComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)2148 inline void handleLogServicesDumpServiceComputerSystemGet(
2149 crow::App& app, const crow::Request& req,
2150 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2151 const std::string& chassisId)
2152 {
2153 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2154 {
2155 return;
2156 }
2157 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2158 {
2159 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2160 return;
2161 }
2162 getDumpServiceInfo(asyncResp, "System");
2163 }
2164
handleLogServicesDumpEntriesCollectionGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2165 inline void handleLogServicesDumpEntriesCollectionGet(
2166 crow::App& app, const std::string& dumpType, const crow::Request& req,
2167 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2168 const std::string& managerId)
2169 {
2170 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2171 {
2172 return;
2173 }
2174
2175 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2176 {
2177 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2178 return;
2179 }
2180 getDumpEntryCollection(asyncResp, dumpType);
2181 }
2182
handleLogServicesDumpEntriesCollectionComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)2183 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet(
2184 crow::App& app, const crow::Request& req,
2185 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2186 const std::string& chassisId)
2187 {
2188 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2189 {
2190 return;
2191 }
2192 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2193 {
2194 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2195 return;
2196 }
2197 getDumpEntryCollection(asyncResp, "System");
2198 }
2199
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)2200 inline void handleLogServicesDumpEntryGet(
2201 crow::App& app, const std::string& dumpType, const crow::Request& req,
2202 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2203 const std::string& managerId, const std::string& dumpId)
2204 {
2205 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2206 {
2207 return;
2208 }
2209 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2210 {
2211 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2212 return;
2213 }
2214 getDumpEntryById(asyncResp, dumpId, dumpType);
2215 }
2216
handleLogServicesDumpEntryComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & dumpId)2217 inline void handleLogServicesDumpEntryComputerSystemGet(
2218 crow::App& app, const crow::Request& req,
2219 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2220 const std::string& chassisId, const std::string& dumpId)
2221 {
2222 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2223 {
2224 return;
2225 }
2226 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2227 {
2228 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2229 return;
2230 }
2231 getDumpEntryById(asyncResp, dumpId, "System");
2232 }
2233
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)2234 inline void handleLogServicesDumpEntryDelete(
2235 crow::App& app, const std::string& dumpType, const crow::Request& req,
2236 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2237 const std::string& managerId, const std::string& dumpId)
2238 {
2239 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2240 {
2241 return;
2242 }
2243
2244 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2245 {
2246 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2247 return;
2248 }
2249 deleteDumpEntry(asyncResp, dumpId, dumpType);
2250 }
2251
handleLogServicesDumpEntryComputerSystemDelete(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & dumpId)2252 inline void handleLogServicesDumpEntryComputerSystemDelete(
2253 crow::App& app, const crow::Request& req,
2254 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2255 const std::string& chassisId, const std::string& dumpId)
2256 {
2257 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2258 {
2259 return;
2260 }
2261 if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2262 {
2263 messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2264 return;
2265 }
2266 deleteDumpEntry(asyncResp, dumpId, "System");
2267 }
2268
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)2269 inline void handleLogServicesDumpEntryDownloadGet(
2270 crow::App& app, const std::string& dumpType, const crow::Request& req,
2271 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2272 const std::string& managerId, const std::string& dumpId)
2273 {
2274 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2275 {
2276 return;
2277 }
2278
2279 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2280 {
2281 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2282 return;
2283 }
2284 downloadDumpEntry(asyncResp, dumpId, dumpType);
2285 }
2286
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)2287 inline void handleDBusEventLogEntryDownloadGet(
2288 crow::App& app, const std::string& dumpType, const crow::Request& req,
2289 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2290 const std::string& systemName, const std::string& entryID)
2291 {
2292 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2293 {
2294 return;
2295 }
2296 if (!http_helpers::isContentTypeAllowed(
2297 req.getHeaderValue("Accept"),
2298 http_helpers::ContentType::OctetStream, true))
2299 {
2300 asyncResp->res.result(boost::beast::http::status::bad_request);
2301 return;
2302 }
2303 downloadEventLogEntry(asyncResp, systemName, entryID, dumpType);
2304 }
2305
handleLogServicesDumpCollectDiagnosticDataPost(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2306 inline void handleLogServicesDumpCollectDiagnosticDataPost(
2307 crow::App& app, const std::string& dumpType, const crow::Request& req,
2308 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2309 const std::string& managerId)
2310 {
2311 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2312 {
2313 return;
2314 }
2315 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2316 {
2317 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2318 return;
2319 }
2320
2321 createDump(asyncResp, req, dumpType);
2322 }
2323
handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)2324 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(
2325 crow::App& app, const crow::Request& req,
2326 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2327 const std::string& systemName)
2328 {
2329 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2330 {
2331 return;
2332 }
2333
2334 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2335 {
2336 // Option currently returns no systems. TBD
2337 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2338 systemName);
2339 return;
2340 }
2341 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2342 {
2343 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2344 systemName);
2345 return;
2346 }
2347 createDump(asyncResp, req, "System");
2348 }
2349
handleLogServicesDumpClearLogPost(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2350 inline void handleLogServicesDumpClearLogPost(
2351 crow::App& app, const std::string& dumpType, const crow::Request& req,
2352 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2353 const std::string& managerId)
2354 {
2355 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2356 {
2357 return;
2358 }
2359
2360 if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2361 {
2362 messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2363 return;
2364 }
2365 clearDump(asyncResp, dumpType);
2366 }
2367
handleLogServicesDumpClearLogComputerSystemPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)2368 inline void handleLogServicesDumpClearLogComputerSystemPost(
2369 crow::App& app, const crow::Request& req,
2370 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2371 const std::string& systemName)
2372 {
2373 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2374 {
2375 return;
2376 }
2377 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2378 {
2379 // Option currently returns no systems. TBD
2380 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2381 systemName);
2382 return;
2383 }
2384 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2385 {
2386 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2387 systemName);
2388 return;
2389 }
2390 clearDump(asyncResp, "System");
2391 }
2392
requestRoutesBMCDumpService(App & app)2393 inline void requestRoutesBMCDumpService(App& app)
2394 {
2395 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/")
2396 .privileges(redfish::privileges::getLogService)
2397 .methods(boost::beast::http::verb::get)(std::bind_front(
2398 handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
2399 }
2400
requestRoutesBMCDumpEntryCollection(App & app)2401 inline void requestRoutesBMCDumpEntryCollection(App& app)
2402 {
2403 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/")
2404 .privileges(redfish::privileges::getLogEntryCollection)
2405 .methods(boost::beast::http::verb::get)(std::bind_front(
2406 handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
2407 }
2408
requestRoutesBMCDumpEntry(App & app)2409 inline void requestRoutesBMCDumpEntry(App& app)
2410 {
2411 BMCWEB_ROUTE(app,
2412 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
2413 .privileges(redfish::privileges::getLogEntry)
2414 .methods(boost::beast::http::verb::get)(std::bind_front(
2415 handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2416
2417 BMCWEB_ROUTE(app,
2418 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
2419 .privileges(redfish::privileges::deleteLogEntry)
2420 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2421 handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
2422 }
2423
requestRoutesBMCDumpEntryDownload(App & app)2424 inline void requestRoutesBMCDumpEntryDownload(App& app)
2425 {
2426 BMCWEB_ROUTE(
2427 app,
2428 "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/")
2429 .privileges(redfish::privileges::getLogEntry)
2430 .methods(boost::beast::http::verb::get)(std::bind_front(
2431 handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC"));
2432 }
2433
requestRoutesBMCDumpCreate(App & app)2434 inline void requestRoutesBMCDumpCreate(App& app)
2435 {
2436 BMCWEB_ROUTE(
2437 app,
2438 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2439 .privileges(redfish::privileges::postLogService)
2440 .methods(boost::beast::http::verb::post)(
2441 std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2442 std::ref(app), "BMC"));
2443 }
2444
requestRoutesBMCDumpClear(App & app)2445 inline void requestRoutesBMCDumpClear(App& app)
2446 {
2447 BMCWEB_ROUTE(
2448 app,
2449 "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2450 .privileges(redfish::privileges::postLogService)
2451 .methods(boost::beast::http::verb::post)(std::bind_front(
2452 handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2453 }
2454
requestRoutesDBusEventLogEntryDownload(App & app)2455 inline void requestRoutesDBusEventLogEntryDownload(App& app)
2456 {
2457 BMCWEB_ROUTE(
2458 app,
2459 "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment/")
2460 .privileges(redfish::privileges::getLogEntry)
2461 .methods(boost::beast::http::verb::get)(std::bind_front(
2462 handleDBusEventLogEntryDownloadGet, std::ref(app), "System"));
2463 }
2464
requestRoutesFaultLogDumpService(App & app)2465 inline void requestRoutesFaultLogDumpService(App& app)
2466 {
2467 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/")
2468 .privileges(redfish::privileges::getLogService)
2469 .methods(boost::beast::http::verb::get)(std::bind_front(
2470 handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2471 }
2472
requestRoutesFaultLogDumpEntryCollection(App & app)2473 inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2474 {
2475 BMCWEB_ROUTE(app,
2476 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/")
2477 .privileges(redfish::privileges::getLogEntryCollection)
2478 .methods(boost::beast::http::verb::get)(
2479 std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2480 std::ref(app), "FaultLog"));
2481 }
2482
requestRoutesFaultLogDumpEntry(App & app)2483 inline void requestRoutesFaultLogDumpEntry(App& app)
2484 {
2485 BMCWEB_ROUTE(
2486 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
2487 .privileges(redfish::privileges::getLogEntry)
2488 .methods(boost::beast::http::verb::get)(std::bind_front(
2489 handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2490
2491 BMCWEB_ROUTE(
2492 app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
2493 .privileges(redfish::privileges::deleteLogEntry)
2494 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2495 handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2496 }
2497
requestRoutesFaultLogDumpClear(App & app)2498 inline void requestRoutesFaultLogDumpClear(App& app)
2499 {
2500 BMCWEB_ROUTE(
2501 app,
2502 "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/")
2503 .privileges(redfish::privileges::postLogService)
2504 .methods(boost::beast::http::verb::post)(std::bind_front(
2505 handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
2506 }
2507
requestRoutesSystemDumpService(App & app)2508 inline void requestRoutesSystemDumpService(App& app)
2509 {
2510 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/")
2511 .privileges(redfish::privileges::getLogService)
2512 .methods(boost::beast::http::verb::get)(std::bind_front(
2513 handleLogServicesDumpServiceComputerSystemGet, std::ref(app)));
2514 }
2515
requestRoutesSystemDumpEntryCollection(App & app)2516 inline void requestRoutesSystemDumpEntryCollection(App& app)
2517 {
2518 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/")
2519 .privileges(redfish::privileges::getLogEntryCollection)
2520 .methods(boost::beast::http::verb::get)(std::bind_front(
2521 handleLogServicesDumpEntriesCollectionComputerSystemGet,
2522 std::ref(app)));
2523 }
2524
requestRoutesSystemDumpEntry(App & app)2525 inline void requestRoutesSystemDumpEntry(App& app)
2526 {
2527 BMCWEB_ROUTE(app,
2528 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2529 .privileges(redfish::privileges::getLogEntry)
2530 .methods(boost::beast::http::verb::get)(std::bind_front(
2531 handleLogServicesDumpEntryComputerSystemGet, std::ref(app)));
2532
2533 BMCWEB_ROUTE(app,
2534 "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2535 .privileges(redfish::privileges::deleteLogEntry)
2536 .methods(boost::beast::http::verb::delete_)(std::bind_front(
2537 handleLogServicesDumpEntryComputerSystemDelete, std::ref(app)));
2538 }
2539
requestRoutesSystemDumpCreate(App & app)2540 inline void requestRoutesSystemDumpCreate(App& app)
2541 {
2542 BMCWEB_ROUTE(
2543 app,
2544 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2545 .privileges(redfish::privileges::postLogService)
2546 .methods(boost::beast::http::verb::post)(std::bind_front(
2547 handleLogServicesDumpCollectDiagnosticDataComputerSystemPost,
2548 std::ref(app)));
2549 }
2550
requestRoutesSystemDumpClear(App & app)2551 inline void requestRoutesSystemDumpClear(App& app)
2552 {
2553 BMCWEB_ROUTE(
2554 app,
2555 "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2556 .privileges(redfish::privileges::postLogService)
2557 .methods(boost::beast::http::verb::post)(std::bind_front(
2558 handleLogServicesDumpClearLogComputerSystemPost, std::ref(app)));
2559 }
2560
requestRoutesCrashdumpService(App & app)2561 inline void requestRoutesCrashdumpService(App& app)
2562 {
2563 // Note: Deviated from redfish privilege registry for GET & HEAD
2564 // method for security reasons.
2565 /**
2566 * Functions triggers appropriate requests on DBus
2567 */
2568 BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/")
2569 // This is incorrect, should be:
2570 //.privileges(redfish::privileges::getLogService)
2571 .privileges({{"ConfigureManager"}})
2572 .methods(
2573 boost::beast::http::verb::
2574 get)([&app](const crow::Request& req,
2575 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2576 const std::string& systemName) {
2577 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2578 {
2579 return;
2580 }
2581 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2582 {
2583 // Option currently returns no systems. TBD
2584 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2585 systemName);
2586 return;
2587 }
2588 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2589 {
2590 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2591 systemName);
2592 return;
2593 }
2594
2595 // Copy over the static data to include the entries added by
2596 // SubRoute
2597 asyncResp->res.jsonValue["@odata.id"] =
2598 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
2599 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2600 asyncResp->res.jsonValue["@odata.type"] =
2601 "#LogService.v1_2_0.LogService";
2602 asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2603 asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2604 asyncResp->res.jsonValue["Id"] = "Crashdump";
2605 asyncResp->res.jsonValue["OverWritePolicy"] =
2606 log_service::OverWritePolicy::WrapsWhenFull;
2607 asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
2608
2609 std::pair<std::string, std::string> redfishDateTimeOffset =
2610 redfish::time_utils::getDateTimeOffsetNow();
2611 asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2612 asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2613 redfishDateTimeOffset.second;
2614
2615 asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
2616 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
2617 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2618 asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
2619 ["target"] = std::format(
2620 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog",
2621 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2622 asyncResp->res
2623 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2624 ["target"] = std::format(
2625 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData",
2626 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2627 });
2628 }
2629
requestRoutesCrashdumpClear(App & app)2630 void inline requestRoutesCrashdumpClear(App& app)
2631 {
2632 BMCWEB_ROUTE(
2633 app,
2634 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/")
2635 // This is incorrect, should be:
2636 //.privileges(redfish::privileges::postLogService)
2637 .privileges({{"ConfigureComponents"}})
2638 .methods(boost::beast::http::verb::post)(
2639 [&app](const crow::Request& req,
2640 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2641 const std::string& systemName) {
2642 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2643 {
2644 return;
2645 }
2646 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2647 {
2648 // Option currently returns no systems. TBD
2649 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2650 systemName);
2651 return;
2652 }
2653 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2654 {
2655 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2656 systemName);
2657 return;
2658 }
2659 crow::connections::systemBus->async_method_call(
2660 [asyncResp](const boost::system::error_code& ec,
2661 const std::string&) {
2662 if (ec)
2663 {
2664 messages::internalError(asyncResp->res);
2665 return;
2666 }
2667 messages::success(asyncResp->res);
2668 },
2669 crashdumpObject, crashdumpPath, deleteAllInterface,
2670 "DeleteAll");
2671 });
2672 }
2673
2674 inline void
logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & logID,nlohmann::json & logEntryJson)2675 logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2676 const std::string& logID, nlohmann::json& logEntryJson)
2677 {
2678 auto getStoredLogCallback =
2679 [asyncResp, logID,
2680 &logEntryJson](const boost::system::error_code& ec,
2681 const dbus::utility::DBusPropertiesMap& params) {
2682 if (ec)
2683 {
2684 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message());
2685 if (ec.value() ==
2686 boost::system::linux_error::bad_request_descriptor)
2687 {
2688 messages::resourceNotFound(asyncResp->res, "LogEntry",
2689 logID);
2690 }
2691 else
2692 {
2693 messages::internalError(asyncResp->res);
2694 }
2695 return;
2696 }
2697
2698 std::string timestamp{};
2699 std::string filename{};
2700 std::string logfile{};
2701 parseCrashdumpParameters(params, filename, timestamp, logfile);
2702
2703 if (filename.empty() || timestamp.empty())
2704 {
2705 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
2706 return;
2707 }
2708
2709 std::string crashdumpURI =
2710 std::format(
2711 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/",
2712 BMCWEB_REDFISH_SYSTEM_URI_NAME) +
2713 logID + "/" + filename;
2714 nlohmann::json::object_t logEntry;
2715 logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
2716 logEntry["@odata.id"] = boost::urls::format(
2717 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}",
2718 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID);
2719 logEntry["Name"] = "CPU Crashdump";
2720 logEntry["Id"] = logID;
2721 logEntry["EntryType"] = log_entry::LogEntryType::Oem;
2722 logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2723 logEntry["DiagnosticDataType"] = "OEM";
2724 logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2725 logEntry["Created"] = std::move(timestamp);
2726
2727 // If logEntryJson references an array of LogEntry resources
2728 // ('Members' list), then push this as a new entry, otherwise set it
2729 // directly
2730 if (logEntryJson.is_array())
2731 {
2732 logEntryJson.push_back(logEntry);
2733 asyncResp->res.jsonValue["Members@odata.count"] =
2734 logEntryJson.size();
2735 }
2736 else
2737 {
2738 logEntryJson.update(logEntry);
2739 }
2740 };
2741 dbus::utility::getAllProperties(
2742 crashdumpObject, crashdumpPath + std::string("/") + logID,
2743 crashdumpInterface, std::move(getStoredLogCallback));
2744 }
2745
requestRoutesCrashdumpEntryCollection(App & app)2746 inline void requestRoutesCrashdumpEntryCollection(App& app)
2747 {
2748 // Note: Deviated from redfish privilege registry for GET & HEAD
2749 // method for security reasons.
2750 /**
2751 * Functions triggers appropriate requests on DBus
2752 */
2753 BMCWEB_ROUTE(app,
2754 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/")
2755 // This is incorrect, should be.
2756 //.privileges(redfish::privileges::postLogEntryCollection)
2757 .privileges({{"ConfigureComponents"}})
2758 .methods(
2759 boost::beast::http::verb::
2760 get)([&app](const crow::Request& req,
2761 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2762 const std::string& systemName) {
2763 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2764 {
2765 return;
2766 }
2767 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2768 {
2769 // Option currently returns no systems. TBD
2770 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2771 systemName);
2772 return;
2773 }
2774 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2775 {
2776 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2777 systemName);
2778 return;
2779 }
2780
2781 constexpr std::array<std::string_view, 1> interfaces = {
2782 crashdumpInterface};
2783 dbus::utility::getSubTreePaths(
2784 "/", 0, interfaces,
2785 [asyncResp](const boost::system::error_code& ec,
2786 const std::vector<std::string>& resp) {
2787 if (ec)
2788 {
2789 if (ec.value() !=
2790 boost::system::errc::no_such_file_or_directory)
2791 {
2792 BMCWEB_LOG_DEBUG("failed to get entries ec: {}",
2793 ec.message());
2794 messages::internalError(asyncResp->res);
2795 return;
2796 }
2797 }
2798 asyncResp->res.jsonValue["@odata.type"] =
2799 "#LogEntryCollection.LogEntryCollection";
2800 asyncResp->res.jsonValue["@odata.id"] = std::format(
2801 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
2802 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2803 asyncResp->res.jsonValue["Name"] =
2804 "Open BMC Crashdump Entries";
2805 asyncResp->res.jsonValue["Description"] =
2806 "Collection of Crashdump Entries";
2807 asyncResp->res.jsonValue["Members"] =
2808 nlohmann::json::array();
2809 asyncResp->res.jsonValue["Members@odata.count"] = 0;
2810
2811 for (const std::string& path : resp)
2812 {
2813 const sdbusplus::message::object_path objPath(path);
2814 // Get the log ID
2815 std::string logID = objPath.filename();
2816 if (logID.empty())
2817 {
2818 continue;
2819 }
2820 // Add the log entry to the array
2821 logCrashdumpEntry(asyncResp, logID,
2822 asyncResp->res.jsonValue["Members"]);
2823 }
2824 });
2825 });
2826 }
2827
requestRoutesCrashdumpEntry(App & app)2828 inline void requestRoutesCrashdumpEntry(App& app)
2829 {
2830 // Note: Deviated from redfish privilege registry for GET & HEAD
2831 // method for security reasons.
2832
2833 BMCWEB_ROUTE(
2834 app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/")
2835 // this is incorrect, should be
2836 // .privileges(redfish::privileges::getLogEntry)
2837 .privileges({{"ConfigureComponents"}})
2838 .methods(boost::beast::http::verb::get)(
2839 [&app](const crow::Request& req,
2840 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2841 const std::string& systemName, const std::string& param) {
2842 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2843 {
2844 return;
2845 }
2846 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2847 {
2848 // Option currently returns no systems. TBD
2849 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2850 systemName);
2851 return;
2852 }
2853 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2854 {
2855 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2856 systemName);
2857 return;
2858 }
2859 const std::string& logID = param;
2860 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2861 });
2862 }
2863
requestRoutesCrashdumpFile(App & app)2864 inline void requestRoutesCrashdumpFile(App& app)
2865 {
2866 // Note: Deviated from redfish privilege registry for GET & HEAD
2867 // method for security reasons.
2868 BMCWEB_ROUTE(
2869 app,
2870 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/")
2871 .privileges(redfish::privileges::getLogEntry)
2872 .methods(boost::beast::http::verb::get)(
2873 [](const crow::Request& req,
2874 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2875 const std::string& systemName, const std::string& logID,
2876 const std::string& fileName) {
2877 // Do not call getRedfishRoute here since the crashdump file is
2878 // not a Redfish resource.
2879
2880 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2881 {
2882 // Option currently returns no systems. TBD
2883 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2884 systemName);
2885 return;
2886 }
2887 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2888 {
2889 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2890 systemName);
2891 return;
2892 }
2893
2894 auto getStoredLogCallback =
2895 [asyncResp, logID, fileName,
2896 url(boost::urls::url(req.url()))](
2897 const boost::system::error_code& ec,
2898 const std::vector<std::pair<
2899 std::string, dbus::utility::DbusVariantType>>&
2900 resp) {
2901 if (ec)
2902 {
2903 BMCWEB_LOG_DEBUG("failed to get log ec: {}",
2904 ec.message());
2905 messages::internalError(asyncResp->res);
2906 return;
2907 }
2908
2909 std::string dbusFilename{};
2910 std::string dbusTimestamp{};
2911 std::string dbusFilepath{};
2912
2913 parseCrashdumpParameters(resp, dbusFilename,
2914 dbusTimestamp, dbusFilepath);
2915
2916 if (dbusFilename.empty() || dbusTimestamp.empty() ||
2917 dbusFilepath.empty())
2918 {
2919 messages::resourceNotFound(asyncResp->res,
2920 "LogEntry", logID);
2921 return;
2922 }
2923
2924 // Verify the file name parameter is correct
2925 if (fileName != dbusFilename)
2926 {
2927 messages::resourceNotFound(asyncResp->res,
2928 "LogEntry", logID);
2929 return;
2930 }
2931
2932 if (asyncResp->res.openFile(dbusFilepath) !=
2933 crow::OpenCode::Success)
2934 {
2935 messages::resourceNotFound(asyncResp->res,
2936 "LogEntry", logID);
2937 return;
2938 }
2939
2940 // Configure this to be a file download when accessed
2941 // from a browser
2942 asyncResp->res.addHeader(
2943 boost::beast::http::field::content_disposition,
2944 "attachment");
2945 };
2946 dbus::utility::getAllProperties(
2947 *crow::connections::systemBus, crashdumpObject,
2948 crashdumpPath + std::string("/") + logID,
2949 crashdumpInterface, std::move(getStoredLogCallback));
2950 });
2951 }
2952
2953 enum class OEMDiagnosticType
2954 {
2955 onDemand,
2956 telemetry,
2957 invalid,
2958 };
2959
getOEMDiagnosticType(std::string_view oemDiagStr)2960 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr)
2961 {
2962 if (oemDiagStr == "OnDemand")
2963 {
2964 return OEMDiagnosticType::onDemand;
2965 }
2966 if (oemDiagStr == "Telemetry")
2967 {
2968 return OEMDiagnosticType::telemetry;
2969 }
2970
2971 return OEMDiagnosticType::invalid;
2972 }
2973
requestRoutesCrashdumpCollect(App & app)2974 inline void requestRoutesCrashdumpCollect(App& app)
2975 {
2976 // Note: Deviated from redfish privilege registry for GET & HEAD
2977 // method for security reasons.
2978 BMCWEB_ROUTE(
2979 app,
2980 "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
2981 // The below is incorrect; Should be ConfigureManager
2982 //.privileges(redfish::privileges::postLogService)
2983 .privileges({{"ConfigureComponents"}})
2984 .methods(boost::beast::http::verb::post)(
2985 [&app](const crow::Request& req,
2986 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2987 const std::string& systemName) {
2988 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2989 {
2990 return;
2991 }
2992
2993 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2994 {
2995 // Option currently returns no systems. TBD
2996 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2997 systemName);
2998 return;
2999 }
3000 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
3001 {
3002 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3003 systemName);
3004 return;
3005 }
3006
3007 std::string diagnosticDataType;
3008 std::string oemDiagnosticDataType;
3009 if (!redfish::json_util::readJsonAction( //
3010 req, asyncResp->res, //
3011 "DiagnosticDataType", diagnosticDataType, //
3012 "OEMDiagnosticDataType", oemDiagnosticDataType //
3013 ))
3014 {
3015 return;
3016 }
3017
3018 if (diagnosticDataType != "OEM")
3019 {
3020 BMCWEB_LOG_ERROR(
3021 "Only OEM DiagnosticDataType supported for Crashdump");
3022 messages::actionParameterValueFormatError(
3023 asyncResp->res, diagnosticDataType,
3024 "DiagnosticDataType", "CollectDiagnosticData");
3025 return;
3026 }
3027
3028 OEMDiagnosticType oemDiagType =
3029 getOEMDiagnosticType(oemDiagnosticDataType);
3030
3031 std::string iface;
3032 std::string method;
3033 std::string taskMatchStr;
3034 if (oemDiagType == OEMDiagnosticType::onDemand)
3035 {
3036 iface = crashdumpOnDemandInterface;
3037 method = "GenerateOnDemandLog";
3038 taskMatchStr =
3039 "type='signal',"
3040 "interface='org.freedesktop.DBus.Properties',"
3041 "member='PropertiesChanged',"
3042 "arg0namespace='com.intel.crashdump'";
3043 }
3044 else if (oemDiagType == OEMDiagnosticType::telemetry)
3045 {
3046 iface = crashdumpTelemetryInterface;
3047 method = "GenerateTelemetryLog";
3048 taskMatchStr =
3049 "type='signal',"
3050 "interface='org.freedesktop.DBus.Properties',"
3051 "member='PropertiesChanged',"
3052 "arg0namespace='com.intel.crashdump'";
3053 }
3054 else
3055 {
3056 BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}",
3057 oemDiagnosticDataType);
3058 messages::actionParameterValueFormatError(
3059 asyncResp->res, oemDiagnosticDataType,
3060 "OEMDiagnosticDataType", "CollectDiagnosticData");
3061 return;
3062 }
3063
3064 auto collectCrashdumpCallback =
3065 [asyncResp, payload(task::Payload(req)),
3066 taskMatchStr](const boost::system::error_code& ec,
3067 const std::string&) mutable {
3068 if (ec)
3069 {
3070 if (ec.value() ==
3071 boost::system::errc::operation_not_supported)
3072 {
3073 messages::resourceInStandby(asyncResp->res);
3074 }
3075 else if (ec.value() == boost::system::errc::
3076 device_or_resource_busy)
3077 {
3078 messages::serviceTemporarilyUnavailable(
3079 asyncResp->res, "60");
3080 }
3081 else
3082 {
3083 messages::internalError(asyncResp->res);
3084 }
3085 return;
3086 }
3087 std::shared_ptr<task::TaskData> task =
3088 task::TaskData::createTask(
3089 [](const boost::system::error_code& ec2,
3090 sdbusplus::message_t&,
3091 const std::shared_ptr<task::TaskData>&
3092 taskData) {
3093 if (!ec2)
3094 {
3095 taskData->messages.emplace_back(
3096 messages::taskCompletedOK(
3097 std::to_string(
3098 taskData->index)));
3099 taskData->state = "Completed";
3100 }
3101 return task::completed;
3102 },
3103 taskMatchStr);
3104
3105 task->startTimer(std::chrono::minutes(5));
3106 task->populateResp(asyncResp->res);
3107 task->payload.emplace(std::move(payload));
3108 };
3109
3110 crow::connections::systemBus->async_method_call(
3111 std::move(collectCrashdumpCallback), crashdumpObject,
3112 crashdumpPath, iface, method);
3113 });
3114 }
3115
dBusLogServiceActionsClear(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)3116 inline void dBusLogServiceActionsClear(
3117 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
3118 {
3119 BMCWEB_LOG_DEBUG("Do delete all entries.");
3120
3121 // Process response from Logging service.
3122 auto respHandler = [asyncResp](const boost::system::error_code& ec) {
3123 BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done");
3124 if (ec)
3125 {
3126 // TODO Handle for specific error code
3127 BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec);
3128 asyncResp->res.result(
3129 boost::beast::http::status::internal_server_error);
3130 return;
3131 }
3132
3133 asyncResp->res.result(boost::beast::http::status::no_content);
3134 };
3135
3136 // Make call to Logging service to request Clear Log
3137 crow::connections::systemBus->async_method_call(
3138 respHandler, "xyz.openbmc_project.Logging",
3139 "/xyz/openbmc_project/logging",
3140 "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3141 }
3142
3143 /**
3144 * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3145 */
requestRoutesDBusLogServiceActionsClear(App & app)3146 inline void requestRoutesDBusLogServiceActionsClear(App& app)
3147 {
3148 /**
3149 * Function handles POST method request.
3150 * The Clear Log actions does not require any parameter.The action deletes
3151 * all entries found in the Entries collection for this Log Service.
3152 */
3153
3154 BMCWEB_ROUTE(
3155 app,
3156 "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
3157 .privileges(redfish::privileges::postLogService)
3158 .methods(boost::beast::http::verb::post)(
3159 [&app](const crow::Request& req,
3160 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3161 const std::string& systemName) {
3162 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3163 {
3164 return;
3165 }
3166 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
3167 {
3168 // Option currently returns no systems. TBD
3169 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3170 systemName);
3171 return;
3172 }
3173 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
3174 {
3175 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3176 systemName);
3177 return;
3178 }
3179 dBusLogServiceActionsClear(asyncResp);
3180 });
3181 }
3182
3183 } // namespace redfish
3184