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