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