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