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