xref: /openbmc/bmcweb/features/redfish/lib/log_services.hpp (revision e2460466d95c98c2da4623892ca0ceff216a9ff2)
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->populateResp(asyncResp->res);
938             task->payload.emplace(payload);
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({{"ConfigureComponents"}})
1348         .methods(boost::beast::http::verb::post)(std::bind_front(
1349             handleSystemsLogServicesEventLogActionsClearPost, std::ref(app)));
1350 }
1351 
1352 enum class LogParseError
1353 {
1354     success,
1355     parseFailed,
1356     messageIdNotInRegistry,
1357 };
1358 
1359 static LogParseError fillEventLogEntryJson(
1360     const std::string& logEntryID, const std::string& logEntry,
1361     nlohmann::json::object_t& logEntryJson)
1362 {
1363     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
1364     // First get the Timestamp
1365     size_t space = logEntry.find_first_of(' ');
1366     if (space == std::string::npos)
1367     {
1368         return LogParseError::parseFailed;
1369     }
1370     std::string timestamp = logEntry.substr(0, space);
1371     // Then get the log contents
1372     size_t entryStart = logEntry.find_first_not_of(' ', space);
1373     if (entryStart == std::string::npos)
1374     {
1375         return LogParseError::parseFailed;
1376     }
1377     std::string_view entry(logEntry);
1378     entry.remove_prefix(entryStart);
1379     // Use split to separate the entry into its fields
1380     std::vector<std::string> logEntryFields;
1381     bmcweb::split(logEntryFields, entry, ',');
1382     // We need at least a MessageId to be valid
1383     auto logEntryIter = logEntryFields.begin();
1384     if (logEntryIter == logEntryFields.end())
1385     {
1386         return LogParseError::parseFailed;
1387     }
1388     std::string& messageID = *logEntryIter;
1389     // Get the Message from the MessageRegistry
1390     const registries::Message* message = registries::getMessage(messageID);
1391 
1392     logEntryIter++;
1393     if (message == nullptr)
1394     {
1395         BMCWEB_LOG_WARNING("Log entry not found in registry: {}", logEntry);
1396         return LogParseError::messageIdNotInRegistry;
1397     }
1398 
1399     std::vector<std::string_view> messageArgs(logEntryIter,
1400                                               logEntryFields.end());
1401     messageArgs.resize(message->numberOfArgs);
1402 
1403     std::string msg =
1404         redfish::registries::fillMessageArgs(messageArgs, message->message);
1405     if (msg.empty())
1406     {
1407         return LogParseError::parseFailed;
1408     }
1409 
1410     // Get the Created time from the timestamp. The log timestamp is in RFC3339
1411     // format which matches the Redfish format except for the fractional seconds
1412     // between the '.' and the '+', so just remove them.
1413     std::size_t dot = timestamp.find_first_of('.');
1414     std::size_t plus = timestamp.find_first_of('+');
1415     if (dot != std::string::npos && plus != std::string::npos)
1416     {
1417         timestamp.erase(dot, plus - dot);
1418     }
1419 
1420     // Fill in the log entry with the gathered data
1421     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1422     logEntryJson["@odata.id"] = boost::urls::format(
1423         "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}",
1424         BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID);
1425     logEntryJson["Name"] = "System Event Log Entry";
1426     logEntryJson["Id"] = logEntryID;
1427     logEntryJson["Message"] = std::move(msg);
1428     logEntryJson["MessageId"] = std::move(messageID);
1429     logEntryJson["MessageArgs"] = messageArgs;
1430     logEntryJson["EntryType"] = "Event";
1431     logEntryJson["Severity"] = message->messageSeverity;
1432     logEntryJson["Created"] = std::move(timestamp);
1433     return LogParseError::success;
1434 }
1435 
1436 inline bool fillEventLogLogEntryFromPropertyMap(
1437     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1438     const dbus::utility::DBusPropertiesMap& resp,
1439     nlohmann::json& objectToFillOut)
1440 {
1441     std::optional<DbusEventLogEntry> optEntry =
1442         fillDbusEventLogEntryFromPropertyMap(resp);
1443 
1444     if (!optEntry.has_value())
1445     {
1446         messages::internalError(asyncResp->res);
1447         return false;
1448     }
1449     DbusEventLogEntry entry = optEntry.value();
1450 
1451     objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1452     objectToFillOut["@odata.id"] = boost::urls::format(
1453         "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}",
1454         BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id));
1455     objectToFillOut["Name"] = "System Event Log Entry";
1456     objectToFillOut["Id"] = std::to_string(entry.Id);
1457     objectToFillOut["Message"] = entry.Message;
1458     objectToFillOut["Resolved"] = entry.Resolved;
1459     std::optional<bool> notifyAction =
1460         getProviderNotifyAction(entry.ServiceProviderNotify);
1461     if (notifyAction)
1462     {
1463         objectToFillOut["ServiceProviderNotified"] = *notifyAction;
1464     }
1465     if ((entry.Resolution != nullptr) && !entry.Resolution->empty())
1466     {
1467         objectToFillOut["Resolution"] = *entry.Resolution;
1468     }
1469     objectToFillOut["EntryType"] = "Event";
1470     objectToFillOut["Severity"] =
1471         translateSeverityDbusToRedfish(entry.Severity);
1472     objectToFillOut["Created"] =
1473         redfish::time_utils::getDateTimeUintMs(entry.Timestamp);
1474     objectToFillOut["Modified"] =
1475         redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp);
1476     if (entry.Path != nullptr)
1477     {
1478         objectToFillOut["AdditionalDataURI"] = boost::urls::format(
1479             "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}/attachment",
1480             BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id));
1481     }
1482     return true;
1483 }
1484 
1485 inline void afterLogEntriesGetManagedObjects(
1486     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1487     const boost::system::error_code& ec,
1488     const dbus::utility::ManagedObjectType& resp)
1489 {
1490     if (ec)
1491     {
1492         // TODO Handle for specific error code
1493         BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}",
1494                          ec);
1495         messages::internalError(asyncResp->res);
1496         return;
1497     }
1498     nlohmann::json::array_t entriesArray;
1499     for (const auto& objectPath : resp)
1500     {
1501         dbus::utility::DBusPropertiesMap propsFlattened;
1502         auto isEntry =
1503             std::ranges::find_if(objectPath.second, [](const auto& object) {
1504                 return object.first == "xyz.openbmc_project.Logging.Entry";
1505             });
1506         if (isEntry == objectPath.second.end())
1507         {
1508             continue;
1509         }
1510         for (const auto& interfaceMap : objectPath.second)
1511         {
1512             for (const auto& propertyMap : interfaceMap.second)
1513             {
1514                 propsFlattened.emplace_back(propertyMap.first,
1515                                             propertyMap.second);
1516             }
1517         }
1518         bool success = fillEventLogLogEntryFromPropertyMap(
1519             asyncResp, propsFlattened, entriesArray.emplace_back());
1520         if (!success)
1521         {
1522             return;
1523         }
1524     }
1525 
1526     redfish::json_util::sortJsonArrayByKey(entriesArray, "Id");
1527     asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
1528     asyncResp->res.jsonValue["Members"] = std::move(entriesArray);
1529 }
1530 
1531 inline void handleSystemsLogServiceEventLogLogEntryCollection(
1532     App& app, const crow::Request& req,
1533     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1534     const std::string& systemName)
1535 {
1536     query_param::QueryCapabilities capabilities = {
1537         .canDelegateTop = true,
1538         .canDelegateSkip = true,
1539     };
1540     query_param::Query delegatedQuery;
1541     if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
1542                                                   delegatedQuery, capabilities))
1543     {
1544         return;
1545     }
1546     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1547     {
1548         // Option currently returns no systems.  TBD
1549         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1550                                    systemName);
1551         return;
1552     }
1553     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1554     {
1555         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1556                                    systemName);
1557         return;
1558     }
1559 
1560     size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
1561     size_t skip = delegatedQuery.skip.value_or(0);
1562 
1563     // Collections don't include the static data added by SubRoute
1564     // because it has a duplicate entry for members
1565     asyncResp->res.jsonValue["@odata.type"] =
1566         "#LogEntryCollection.LogEntryCollection";
1567     asyncResp->res.jsonValue["@odata.id"] =
1568         std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1569                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
1570     asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1571     asyncResp->res.jsonValue["Description"] =
1572         "Collection of System Event Log Entries";
1573 
1574     nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1575     logEntryArray = nlohmann::json::array();
1576     // Go through the log files and create a unique ID for each
1577     // entry
1578     std::vector<std::filesystem::path> redfishLogFiles;
1579     getRedfishLogFiles(redfishLogFiles);
1580     uint64_t entryCount = 0;
1581     std::string logEntry;
1582 
1583     // Oldest logs are in the last file, so start there and loop
1584     // backwards
1585     for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
1586     {
1587         std::ifstream logStream(*it);
1588         if (!logStream.is_open())
1589         {
1590             continue;
1591         }
1592 
1593         // Reset the unique ID on the first entry
1594         bool firstEntry = true;
1595         while (std::getline(logStream, logEntry))
1596         {
1597             std::string idStr;
1598             if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1599             {
1600                 continue;
1601             }
1602             firstEntry = false;
1603 
1604             nlohmann::json::object_t bmcLogEntry;
1605             LogParseError status =
1606                 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1607             if (status == LogParseError::messageIdNotInRegistry)
1608             {
1609                 continue;
1610             }
1611             if (status != LogParseError::success)
1612             {
1613                 messages::internalError(asyncResp->res);
1614                 return;
1615             }
1616 
1617             entryCount++;
1618             // Handle paging using skip (number of entries to skip from the
1619             // start) and top (number of entries to display)
1620             if (entryCount <= skip || entryCount > skip + top)
1621             {
1622                 continue;
1623             }
1624 
1625             logEntryArray.emplace_back(std::move(bmcLogEntry));
1626         }
1627     }
1628     asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1629     if (skip + top < entryCount)
1630     {
1631         asyncResp->res.jsonValue["Members@odata.nextLink"] =
1632             boost::urls::format(
1633                 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries?$skip={}",
1634                 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(skip + top));
1635     }
1636 }
1637 
1638 inline void requestRoutesJournalEventLogEntryCollection(App& app)
1639 {
1640     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1641         .privileges(redfish::privileges::getLogEntryCollection)
1642         .methods(boost::beast::http::verb::get)(std::bind_front(
1643             handleSystemsLogServiceEventLogLogEntryCollection, std::ref(app)));
1644 }
1645 
1646 inline void handleSystemsLogServiceEventLogEntriesGet(
1647     App& app, const crow::Request& req,
1648     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1649     const std::string& systemName, const std::string& param)
1650 {
1651     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1652     {
1653         return;
1654     }
1655     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1656     {
1657         // Option currently returns no systems.  TBD
1658         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1659                                    systemName);
1660         return;
1661     }
1662 
1663     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1664     {
1665         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1666                                    systemName);
1667         return;
1668     }
1669 
1670     const std::string& targetID = param;
1671 
1672     // Go through the log files and check the unique ID for each
1673     // entry to find the target entry
1674     std::vector<std::filesystem::path> redfishLogFiles;
1675     getRedfishLogFiles(redfishLogFiles);
1676     std::string logEntry;
1677 
1678     // Oldest logs are in the last file, so start there and loop
1679     // backwards
1680     for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
1681     {
1682         std::ifstream logStream(*it);
1683         if (!logStream.is_open())
1684         {
1685             continue;
1686         }
1687 
1688         // Reset the unique ID on the first entry
1689         bool firstEntry = true;
1690         while (std::getline(logStream, logEntry))
1691         {
1692             std::string idStr;
1693             if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1694             {
1695                 continue;
1696             }
1697             firstEntry = false;
1698 
1699             if (idStr == targetID)
1700             {
1701                 nlohmann::json::object_t bmcLogEntry;
1702                 LogParseError status =
1703                     fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1704                 if (status != LogParseError::success)
1705                 {
1706                     messages::internalError(asyncResp->res);
1707                     return;
1708                 }
1709                 asyncResp->res.jsonValue.update(bmcLogEntry);
1710                 return;
1711             }
1712         }
1713     }
1714     // Requested ID was not found
1715     messages::resourceNotFound(asyncResp->res, "LogEntry", targetID);
1716 }
1717 
1718 inline void requestRoutesJournalEventLogEntry(App& app)
1719 {
1720     BMCWEB_ROUTE(
1721         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1722         .privileges(redfish::privileges::getLogEntry)
1723         .methods(boost::beast::http::verb::get)(std::bind_front(
1724             handleSystemsLogServiceEventLogEntriesGet, std::ref(app)));
1725 }
1726 
1727 inline void dBusEventLogEntryCollection(
1728     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1729 {
1730     // Collections don't include the static data added by SubRoute
1731     // because it has a duplicate entry for members
1732     asyncResp->res.jsonValue["@odata.type"] =
1733         "#LogEntryCollection.LogEntryCollection";
1734     asyncResp->res.jsonValue["@odata.id"] =
1735         std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1736                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
1737     asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1738     asyncResp->res.jsonValue["Description"] =
1739         "Collection of System Event Log Entries";
1740 
1741     // DBus implementation of EventLog/Entries
1742     // Make call to Logging Service to find all log entry objects
1743     sdbusplus::message::object_path path("/xyz/openbmc_project/logging");
1744     dbus::utility::getManagedObjects(
1745         "xyz.openbmc_project.Logging", path,
1746         [asyncResp](const boost::system::error_code& ec,
1747                     const dbus::utility::ManagedObjectType& resp) {
1748             afterLogEntriesGetManagedObjects(asyncResp, ec, resp);
1749         });
1750 }
1751 
1752 inline void requestRoutesDBusEventLogEntryCollection(App& app)
1753 {
1754     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1755         .privileges(redfish::privileges::getLogEntryCollection)
1756         .methods(boost::beast::http::verb::get)(
1757             [&app](const crow::Request& req,
1758                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1759                    const std::string& systemName) {
1760                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1761                 {
1762                     return;
1763                 }
1764                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1765                 {
1766                     // Option currently returns no systems.  TBD
1767                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1768                                                systemName);
1769                     return;
1770                 }
1771                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1772                 {
1773                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1774                                                systemName);
1775                     return;
1776                 }
1777                 dBusEventLogEntryCollection(asyncResp);
1778             });
1779 }
1780 
1781 inline void dBusEventLogEntryGet(
1782     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
1783 {
1784     dbus::utility::escapePathForDbus(entryID);
1785 
1786     // DBus implementation of EventLog/Entries
1787     // Make call to Logging Service to find all log entry objects
1788     dbus::utility::getAllProperties(
1789         "xyz.openbmc_project.Logging",
1790         "/xyz/openbmc_project/logging/entry/" + entryID, "",
1791         [asyncResp, entryID](const boost::system::error_code& ec,
1792                              const dbus::utility::DBusPropertiesMap& resp) {
1793             if (ec.value() == EBADR)
1794             {
1795                 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1796                                            entryID);
1797                 return;
1798             }
1799             if (ec)
1800             {
1801                 BMCWEB_LOG_ERROR(
1802                     "EventLogEntry (DBus) resp_handler got error {}", ec);
1803                 messages::internalError(asyncResp->res);
1804                 return;
1805             }
1806 
1807             fillEventLogLogEntryFromPropertyMap(asyncResp, resp,
1808                                                 asyncResp->res.jsonValue);
1809         });
1810 }
1811 
1812 inline void dBusEventLogEntryPatch(
1813     const crow::Request& req,
1814     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1815     const std::string& entryId)
1816 {
1817     std::optional<bool> resolved;
1818 
1819     if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved))
1820     {
1821         return;
1822     }
1823     BMCWEB_LOG_DEBUG("Set Resolved");
1824 
1825     setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging",
1826                     "/xyz/openbmc_project/logging/entry/" + entryId,
1827                     "xyz.openbmc_project.Logging.Entry", "Resolved",
1828                     resolved.value_or(false));
1829 }
1830 
1831 inline void dBusEventLogEntryDelete(
1832     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
1833 {
1834     BMCWEB_LOG_DEBUG("Do delete single event entries.");
1835 
1836     dbus::utility::escapePathForDbus(entryID);
1837 
1838     // Process response from Logging service.
1839     auto respHandler = [asyncResp,
1840                         entryID](const boost::system::error_code& ec) {
1841         BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done");
1842         if (ec)
1843         {
1844             if (ec.value() == EBADR)
1845             {
1846                 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
1847                 return;
1848             }
1849             // TODO Handle for specific error code
1850             BMCWEB_LOG_ERROR(
1851                 "EventLogEntry (DBus) doDelete respHandler got error {}", ec);
1852             asyncResp->res.result(
1853                 boost::beast::http::status::internal_server_error);
1854             return;
1855         }
1856 
1857         asyncResp->res.result(boost::beast::http::status::ok);
1858     };
1859 
1860     // Make call to Logging service to request Delete Log
1861     dbus::utility::async_method_call(
1862         asyncResp, respHandler, "xyz.openbmc_project.Logging",
1863         "/xyz/openbmc_project/logging/entry/" + entryID,
1864         "xyz.openbmc_project.Object.Delete", "Delete");
1865 }
1866 
1867 inline void requestRoutesDBusEventLogEntry(App& app)
1868 {
1869     BMCWEB_ROUTE(
1870         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1871         .privileges(redfish::privileges::getLogEntry)
1872         .methods(boost::beast::http::verb::get)(
1873             [&app](const crow::Request& req,
1874                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1875                    const std::string& systemName, const std::string& entryId) {
1876                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1877                 {
1878                     return;
1879                 }
1880                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1881                 {
1882                     // Option currently returns no systems.  TBD
1883                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1884                                                systemName);
1885                     return;
1886                 }
1887                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1888                 {
1889                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1890                                                systemName);
1891                     return;
1892                 }
1893 
1894                 dBusEventLogEntryGet(asyncResp, entryId);
1895             });
1896 
1897     BMCWEB_ROUTE(
1898         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1899         .privileges(redfish::privileges::patchLogEntry)
1900         .methods(boost::beast::http::verb::patch)(
1901             [&app](const crow::Request& req,
1902                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1903                    const std::string& systemName, const std::string& entryId) {
1904                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1905                 {
1906                     return;
1907                 }
1908                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1909                 {
1910                     // Option currently returns no systems.  TBD
1911                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1912                                                systemName);
1913                     return;
1914                 }
1915                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1916                 {
1917                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1918                                                systemName);
1919                     return;
1920                 }
1921 
1922                 dBusEventLogEntryPatch(req, asyncResp, entryId);
1923             });
1924 
1925     BMCWEB_ROUTE(
1926         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1927         .privileges(redfish::privileges::deleteLogEntry)
1928 
1929         .methods(boost::beast::http::verb::delete_)(
1930             [&app](const crow::Request& req,
1931                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1932                    const std::string& systemName, const std::string& param) {
1933                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1934                 {
1935                     return;
1936                 }
1937                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1938                 {
1939                     // Option currently returns no systems.  TBD
1940                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1941                                                systemName);
1942                     return;
1943                 }
1944                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1945                 {
1946                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1947                                                systemName);
1948                     return;
1949                 }
1950                 dBusEventLogEntryDelete(asyncResp, param);
1951             });
1952 }
1953 
1954 inline void handleBMCLogServicesCollectionGet(
1955     crow::App& app, const crow::Request& req,
1956     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1957     const std::string& managerId)
1958 {
1959     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1960     {
1961         return;
1962     }
1963 
1964     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1965     {
1966         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1967         return;
1968     }
1969 
1970     // Collections don't include the static data added by SubRoute
1971     // because it has a duplicate entry for members
1972     asyncResp->res.jsonValue["@odata.type"] =
1973         "#LogServiceCollection.LogServiceCollection";
1974     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1975         "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME);
1976     asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1977     asyncResp->res.jsonValue["Description"] =
1978         "Collection of LogServices for this Manager";
1979     nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
1980     logServiceArray = nlohmann::json::array();
1981 
1982     if constexpr (BMCWEB_REDFISH_BMC_JOURNAL)
1983     {
1984         nlohmann::json::object_t journal;
1985         journal["@odata.id"] =
1986             boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal",
1987                                 BMCWEB_REDFISH_MANAGER_URI_NAME);
1988         logServiceArray.emplace_back(std::move(journal));
1989     }
1990 
1991     asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
1992 
1993     if constexpr (BMCWEB_REDFISH_DUMP_LOG)
1994     {
1995         constexpr std::array<std::string_view, 1> interfaces = {
1996             "xyz.openbmc_project.Collection.DeleteAll"};
1997         dbus::utility::getSubTreePaths(
1998             "/xyz/openbmc_project/dump", 0, interfaces,
1999             [asyncResp](const boost::system::error_code& ec,
2000                         const dbus::utility::MapperGetSubTreePathsResponse&
2001                             subTreePaths) {
2002                 if (ec)
2003                 {
2004                     BMCWEB_LOG_ERROR(
2005                         "handleBMCLogServicesCollectionGet respHandler got error {}",
2006                         ec);
2007                     // Assume that getting an error simply means there are no
2008                     // dump LogServices. Return without adding any error
2009                     // response.
2010                     return;
2011                 }
2012 
2013                 nlohmann::json& logServiceArrayLocal =
2014                     asyncResp->res.jsonValue["Members"];
2015 
2016                 for (const std::string& path : subTreePaths)
2017                 {
2018                     if (path == "/xyz/openbmc_project/dump/bmc")
2019                     {
2020                         nlohmann::json::object_t member;
2021                         member["@odata.id"] = boost::urls::format(
2022                             "/redfish/v1/Managers/{}/LogServices/Dump",
2023                             BMCWEB_REDFISH_MANAGER_URI_NAME);
2024                         logServiceArrayLocal.emplace_back(std::move(member));
2025                     }
2026                     else if (path == "/xyz/openbmc_project/dump/faultlog")
2027                     {
2028                         nlohmann::json::object_t member;
2029                         member["@odata.id"] = boost::urls::format(
2030                             "/redfish/v1/Managers/{}/LogServices/FaultLog",
2031                             BMCWEB_REDFISH_MANAGER_URI_NAME);
2032                         logServiceArrayLocal.emplace_back(std::move(member));
2033                     }
2034                 }
2035 
2036                 asyncResp->res.jsonValue["Members@odata.count"] =
2037                     logServiceArrayLocal.size();
2038             });
2039     }
2040 }
2041 
2042 inline void requestRoutesBMCLogServiceCollection(App& app)
2043 {
2044     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/")
2045         .privileges(redfish::privileges::getLogServiceCollection)
2046         .methods(boost::beast::http::verb::get)(
2047             std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app)));
2048 }
2049 
2050 inline void getDumpServiceInfo(
2051     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2052     const std::string& dumpType)
2053 {
2054     std::string dumpPath;
2055     log_service::OverWritePolicy overWritePolicy =
2056         log_service::OverWritePolicy::Invalid;
2057     bool collectDiagnosticDataSupported = false;
2058 
2059     if (dumpType == "BMC")
2060     {
2061         dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump",
2062                                BMCWEB_REDFISH_MANAGER_URI_NAME);
2063         overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
2064         collectDiagnosticDataSupported = true;
2065     }
2066     else if (dumpType == "FaultLog")
2067     {
2068         dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog",
2069                                BMCWEB_REDFISH_MANAGER_URI_NAME);
2070         overWritePolicy = log_service::OverWritePolicy::Unknown;
2071         collectDiagnosticDataSupported = false;
2072     }
2073     else if (dumpType == "System")
2074     {
2075         dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump",
2076                                BMCWEB_REDFISH_SYSTEM_URI_NAME);
2077         overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
2078         collectDiagnosticDataSupported = true;
2079     }
2080     else
2081     {
2082         BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}",
2083                          dumpType);
2084         messages::internalError(asyncResp->res);
2085         return;
2086     }
2087 
2088     asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2089     asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2090     asyncResp->res.jsonValue["Name"] = "Dump LogService";
2091     asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2092     asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2093     asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy;
2094 
2095     std::pair<std::string, std::string> redfishDateTimeOffset =
2096         redfish::time_utils::getDateTimeOffsetNow();
2097     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2098     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2099         redfishDateTimeOffset.second;
2100 
2101     asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2102 
2103     if (collectDiagnosticDataSupported)
2104     {
2105         asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2106                                 ["target"] =
2107             dumpPath + "/Actions/LogService.CollectDiagnosticData";
2108     }
2109 
2110     constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface};
2111     dbus::utility::getSubTreePaths(
2112         "/xyz/openbmc_project/dump", 0, interfaces,
2113         [asyncResp, dumpType, dumpPath](
2114             const boost::system::error_code& ec,
2115             const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2116             if (ec)
2117             {
2118                 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}",
2119                                  ec);
2120                 // Assume that getting an error simply means there are no dump
2121                 // LogServices. Return without adding any error response.
2122                 return;
2123             }
2124             std::string dbusDumpPath = getDumpPath(dumpType);
2125             for (const std::string& path : subTreePaths)
2126             {
2127                 if (path == dbusDumpPath)
2128                 {
2129                     asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
2130                                             ["target"] =
2131                         dumpPath + "/Actions/LogService.ClearLog";
2132                     break;
2133                 }
2134             }
2135         });
2136 }
2137 
2138 inline void handleLogServicesDumpServiceGet(
2139     crow::App& app, const std::string& dumpType, const crow::Request& req,
2140     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2141     const std::string& managerId)
2142 {
2143     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2144     {
2145         return;
2146     }
2147 
2148     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2149     {
2150         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2151         return;
2152     }
2153 
2154     getDumpServiceInfo(asyncResp, dumpType);
2155 }
2156 
2157 inline void handleLogServicesDumpServiceComputerSystemGet(
2158     crow::App& app, const crow::Request& req,
2159     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2160     const std::string& chassisId)
2161 {
2162     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2163     {
2164         return;
2165     }
2166     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2167     {
2168         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2169         return;
2170     }
2171     getDumpServiceInfo(asyncResp, "System");
2172 }
2173 
2174 inline void handleLogServicesDumpEntriesCollectionGet(
2175     crow::App& app, const std::string& dumpType, const crow::Request& req,
2176     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2177     const std::string& managerId)
2178 {
2179     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2180     {
2181         return;
2182     }
2183 
2184     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2185     {
2186         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2187         return;
2188     }
2189     getDumpEntryCollection(asyncResp, dumpType);
2190 }
2191 
2192 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet(
2193     crow::App& app, const crow::Request& req,
2194     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2195     const std::string& chassisId)
2196 {
2197     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2198     {
2199         return;
2200     }
2201     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2202     {
2203         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2204         return;
2205     }
2206     getDumpEntryCollection(asyncResp, "System");
2207 }
2208 
2209 inline void handleLogServicesDumpEntryGet(
2210     crow::App& app, const std::string& dumpType, const crow::Request& req,
2211     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2212     const std::string& managerId, const std::string& dumpId)
2213 {
2214     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2215     {
2216         return;
2217     }
2218     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2219     {
2220         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2221         return;
2222     }
2223     getDumpEntryById(asyncResp, dumpId, dumpType);
2224 }
2225 
2226 inline void handleLogServicesDumpEntryComputerSystemGet(
2227     crow::App& app, const crow::Request& req,
2228     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2229     const std::string& chassisId, const std::string& dumpId)
2230 {
2231     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2232     {
2233         return;
2234     }
2235     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2236     {
2237         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2238         return;
2239     }
2240     getDumpEntryById(asyncResp, dumpId, "System");
2241 }
2242 
2243 inline void handleLogServicesDumpEntryDelete(
2244     crow::App& app, const std::string& dumpType, const crow::Request& req,
2245     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2246     const std::string& managerId, const std::string& dumpId)
2247 {
2248     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2249     {
2250         return;
2251     }
2252 
2253     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2254     {
2255         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2256         return;
2257     }
2258     deleteDumpEntry(asyncResp, dumpId, dumpType);
2259 }
2260 
2261 inline void handleLogServicesDumpEntryComputerSystemDelete(
2262     crow::App& app, const crow::Request& req,
2263     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2264     const std::string& chassisId, const std::string& dumpId)
2265 {
2266     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2267     {
2268         return;
2269     }
2270     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2271     {
2272         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2273         return;
2274     }
2275     deleteDumpEntry(asyncResp, dumpId, "System");
2276 }
2277 
2278 inline void handleLogServicesDumpEntryDownloadGet(
2279     crow::App& app, const std::string& dumpType, const crow::Request& req,
2280     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2281     const std::string& managerId, const std::string& dumpId)
2282 {
2283     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2284     {
2285         return;
2286     }
2287 
2288     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2289     {
2290         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2291         return;
2292     }
2293     downloadDumpEntry(asyncResp, dumpId, dumpType);
2294 }
2295 
2296 inline void handleDBusEventLogEntryDownloadGet(
2297     crow::App& app, const std::string& dumpType, const crow::Request& req,
2298     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2299     const std::string& systemName, const std::string& entryID)
2300 {
2301     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2302     {
2303         return;
2304     }
2305     if (!http_helpers::isContentTypeAllowed(
2306             req.getHeaderValue("Accept"),
2307             http_helpers::ContentType::OctetStream, true))
2308     {
2309         asyncResp->res.result(boost::beast::http::status::bad_request);
2310         return;
2311     }
2312     downloadEventLogEntry(asyncResp, systemName, entryID, dumpType);
2313 }
2314 
2315 inline void handleLogServicesDumpCollectDiagnosticDataPost(
2316     crow::App& app, const std::string& dumpType, const crow::Request& req,
2317     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2318     const std::string& managerId)
2319 {
2320     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2321     {
2322         return;
2323     }
2324     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2325     {
2326         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2327         return;
2328     }
2329 
2330     createDump(asyncResp, req, dumpType);
2331 }
2332 
2333 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(
2334     crow::App& app, const crow::Request& req,
2335     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2336     const std::string& systemName)
2337 {
2338     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2339     {
2340         return;
2341     }
2342 
2343     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2344     {
2345         // Option currently returns no systems.  TBD
2346         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2347                                    systemName);
2348         return;
2349     }
2350     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2351     {
2352         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2353                                    systemName);
2354         return;
2355     }
2356     createDump(asyncResp, req, "System");
2357 }
2358 
2359 inline void handleLogServicesDumpClearLogPost(
2360     crow::App& app, const std::string& dumpType, const crow::Request& req,
2361     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2362     const std::string& managerId)
2363 {
2364     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2365     {
2366         return;
2367     }
2368 
2369     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2370     {
2371         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2372         return;
2373     }
2374     clearDump(asyncResp, dumpType);
2375 }
2376 
2377 inline void handleLogServicesDumpClearLogComputerSystemPost(
2378     crow::App& app, const crow::Request& req,
2379     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2380     const std::string& systemName)
2381 {
2382     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2383     {
2384         return;
2385     }
2386     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2387     {
2388         // Option currently returns no systems.  TBD
2389         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2390                                    systemName);
2391         return;
2392     }
2393     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2394     {
2395         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2396                                    systemName);
2397         return;
2398     }
2399     clearDump(asyncResp, "System");
2400 }
2401 
2402 inline void requestRoutesBMCDumpService(App& app)
2403 {
2404     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/")
2405         .privileges(redfish::privileges::getLogService)
2406         .methods(boost::beast::http::verb::get)(std::bind_front(
2407             handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
2408 }
2409 
2410 inline void requestRoutesBMCDumpEntryCollection(App& app)
2411 {
2412     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/")
2413         .privileges(redfish::privileges::getLogEntryCollection)
2414         .methods(boost::beast::http::verb::get)(std::bind_front(
2415             handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
2416 }
2417 
2418 inline void requestRoutesBMCDumpEntry(App& app)
2419 {
2420     BMCWEB_ROUTE(app,
2421                  "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
2422         .privileges(redfish::privileges::getLogEntry)
2423         .methods(boost::beast::http::verb::get)(std::bind_front(
2424             handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2425 
2426     BMCWEB_ROUTE(app,
2427                  "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
2428         .privileges(redfish::privileges::deleteLogEntry)
2429         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2430             handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
2431 }
2432 
2433 inline void requestRoutesBMCDumpEntryDownload(App& app)
2434 {
2435     BMCWEB_ROUTE(
2436         app,
2437         "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/")
2438         .privileges(redfish::privileges::getLogEntry)
2439         .methods(boost::beast::http::verb::get)(std::bind_front(
2440             handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC"));
2441 }
2442 
2443 inline void requestRoutesBMCDumpCreate(App& app)
2444 {
2445     BMCWEB_ROUTE(
2446         app,
2447         "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2448         .privileges(redfish::privileges::postLogService)
2449         .methods(boost::beast::http::verb::post)(
2450             std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2451                             std::ref(app), "BMC"));
2452 }
2453 
2454 inline void requestRoutesBMCDumpClear(App& app)
2455 {
2456     BMCWEB_ROUTE(
2457         app,
2458         "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2459         .privileges(redfish::privileges::postLogService)
2460         .methods(boost::beast::http::verb::post)(std::bind_front(
2461             handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2462 }
2463 
2464 inline void requestRoutesDBusEventLogEntryDownload(App& app)
2465 {
2466     BMCWEB_ROUTE(
2467         app,
2468         "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment/")
2469         .privileges(redfish::privileges::getLogEntry)
2470         .methods(boost::beast::http::verb::get)(std::bind_front(
2471             handleDBusEventLogEntryDownloadGet, std::ref(app), "System"));
2472 }
2473 
2474 inline void requestRoutesFaultLogDumpService(App& app)
2475 {
2476     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/")
2477         .privileges(redfish::privileges::getLogService)
2478         .methods(boost::beast::http::verb::get)(std::bind_front(
2479             handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2480 }
2481 
2482 inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2483 {
2484     BMCWEB_ROUTE(app,
2485                  "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/")
2486         .privileges(redfish::privileges::getLogEntryCollection)
2487         .methods(boost::beast::http::verb::get)(
2488             std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2489                             std::ref(app), "FaultLog"));
2490 }
2491 
2492 inline void requestRoutesFaultLogDumpEntry(App& app)
2493 {
2494     BMCWEB_ROUTE(
2495         app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
2496         .privileges(redfish::privileges::getLogEntry)
2497         .methods(boost::beast::http::verb::get)(std::bind_front(
2498             handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2499 
2500     BMCWEB_ROUTE(
2501         app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
2502         .privileges(redfish::privileges::deleteLogEntry)
2503         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2504             handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2505 }
2506 
2507 inline void requestRoutesFaultLogDumpClear(App& app)
2508 {
2509     BMCWEB_ROUTE(
2510         app,
2511         "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/")
2512         .privileges(redfish::privileges::postLogService)
2513         .methods(boost::beast::http::verb::post)(std::bind_front(
2514             handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
2515 }
2516 
2517 inline void requestRoutesSystemDumpService(App& app)
2518 {
2519     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/")
2520         .privileges(redfish::privileges::getLogService)
2521         .methods(boost::beast::http::verb::get)(std::bind_front(
2522             handleLogServicesDumpServiceComputerSystemGet, std::ref(app)));
2523 }
2524 
2525 inline void requestRoutesSystemDumpEntryCollection(App& app)
2526 {
2527     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/")
2528         .privileges(redfish::privileges::getLogEntryCollection)
2529         .methods(boost::beast::http::verb::get)(std::bind_front(
2530             handleLogServicesDumpEntriesCollectionComputerSystemGet,
2531             std::ref(app)));
2532 }
2533 
2534 inline void requestRoutesSystemDumpEntry(App& app)
2535 {
2536     BMCWEB_ROUTE(app,
2537                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2538         .privileges(redfish::privileges::getLogEntry)
2539         .methods(boost::beast::http::verb::get)(std::bind_front(
2540             handleLogServicesDumpEntryComputerSystemGet, std::ref(app)));
2541 
2542     BMCWEB_ROUTE(app,
2543                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2544         .privileges(redfish::privileges::deleteLogEntry)
2545         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2546             handleLogServicesDumpEntryComputerSystemDelete, std::ref(app)));
2547 }
2548 
2549 inline void requestRoutesSystemDumpCreate(App& app)
2550 {
2551     BMCWEB_ROUTE(
2552         app,
2553         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2554         .privileges(redfish::privileges::postLogService)
2555         .methods(boost::beast::http::verb::post)(std::bind_front(
2556             handleLogServicesDumpCollectDiagnosticDataComputerSystemPost,
2557             std::ref(app)));
2558 }
2559 
2560 inline void requestRoutesSystemDumpClear(App& app)
2561 {
2562     BMCWEB_ROUTE(
2563         app,
2564         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2565         .privileges(redfish::privileges::postLogService)
2566         .methods(boost::beast::http::verb::post)(std::bind_front(
2567             handleLogServicesDumpClearLogComputerSystemPost, std::ref(app)));
2568 }
2569 
2570 inline void requestRoutesCrashdumpService(App& app)
2571 {
2572     // Note: Deviated from redfish privilege registry for GET & HEAD
2573     // method for security reasons.
2574     /**
2575      * Functions triggers appropriate requests on DBus
2576      */
2577     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/")
2578         // This is incorrect, should be:
2579         //.privileges(redfish::privileges::getLogService)
2580         .privileges({{"ConfigureManager"}})
2581         .methods(
2582             boost::beast::http::verb::
2583                 get)([&app](const crow::Request& req,
2584                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2585                             const std::string& systemName) {
2586             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2587             {
2588                 return;
2589             }
2590             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2591             {
2592                 // Option currently returns no systems.  TBD
2593                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2594                                            systemName);
2595                 return;
2596             }
2597             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2598             {
2599                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2600                                            systemName);
2601                 return;
2602             }
2603 
2604             // Copy over the static data to include the entries added by
2605             // SubRoute
2606             asyncResp->res.jsonValue["@odata.id"] =
2607                 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
2608                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
2609             asyncResp->res.jsonValue["@odata.type"] =
2610                 "#LogService.v1_2_0.LogService";
2611             asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2612             asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2613             asyncResp->res.jsonValue["Id"] = "Crashdump";
2614             asyncResp->res.jsonValue["OverWritePolicy"] =
2615                 log_service::OverWritePolicy::WrapsWhenFull;
2616             asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
2617 
2618             std::pair<std::string, std::string> redfishDateTimeOffset =
2619                 redfish::time_utils::getDateTimeOffsetNow();
2620             asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2621             asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2622                 redfishDateTimeOffset.second;
2623 
2624             asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
2625                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
2626                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2627             asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
2628                                     ["target"] = std::format(
2629                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog",
2630                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2631             asyncResp->res
2632                 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2633                           ["target"] = std::format(
2634                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData",
2635                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2636         });
2637 }
2638 
2639 void inline requestRoutesCrashdumpClear(App& app)
2640 {
2641     BMCWEB_ROUTE(
2642         app,
2643         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/")
2644         // This is incorrect, should be:
2645         //.privileges(redfish::privileges::postLogService)
2646         .privileges({{"ConfigureComponents"}})
2647         .methods(boost::beast::http::verb::post)(
2648             [&app](const crow::Request& req,
2649                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2650                    const std::string& systemName) {
2651                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2652                 {
2653                     return;
2654                 }
2655                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2656                 {
2657                     // Option currently returns no systems.  TBD
2658                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2659                                                systemName);
2660                     return;
2661                 }
2662                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2663                 {
2664                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2665                                                systemName);
2666                     return;
2667                 }
2668                 dbus::utility::async_method_call(
2669                     asyncResp,
2670                     [asyncResp](const boost::system::error_code& ec,
2671                                 const std::string&) {
2672                         if (ec)
2673                         {
2674                             messages::internalError(asyncResp->res);
2675                             return;
2676                         }
2677                         messages::success(asyncResp->res);
2678                     },
2679                     crashdumpObject, crashdumpPath, deleteAllInterface,
2680                     "DeleteAll");
2681             });
2682 }
2683 
2684 inline void logCrashdumpEntry(
2685     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2686     const std::string& logID, nlohmann::json& logEntryJson)
2687 {
2688     auto getStoredLogCallback =
2689         [asyncResp, logID,
2690          &logEntryJson](const boost::system::error_code& ec,
2691                         const dbus::utility::DBusPropertiesMap& params) {
2692             if (ec)
2693             {
2694                 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message());
2695                 if (ec.value() ==
2696                     boost::system::linux_error::bad_request_descriptor)
2697                 {
2698                     messages::resourceNotFound(asyncResp->res, "LogEntry",
2699                                                logID);
2700                 }
2701                 else
2702                 {
2703                     messages::internalError(asyncResp->res);
2704                 }
2705                 return;
2706             }
2707 
2708             std::string timestamp{};
2709             std::string filename{};
2710             std::string logfile{};
2711             parseCrashdumpParameters(params, filename, timestamp, logfile);
2712 
2713             if (filename.empty() || timestamp.empty())
2714             {
2715                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
2716                 return;
2717             }
2718 
2719             std::string crashdumpURI =
2720                 std::format(
2721                     "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/",
2722                     BMCWEB_REDFISH_SYSTEM_URI_NAME) +
2723                 logID + "/" + filename;
2724             nlohmann::json::object_t logEntry;
2725             logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
2726             logEntry["@odata.id"] = boost::urls::format(
2727                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}",
2728                 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID);
2729             logEntry["Name"] = "CPU Crashdump";
2730             logEntry["Id"] = logID;
2731             logEntry["EntryType"] = log_entry::LogEntryType::Oem;
2732             logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2733             logEntry["DiagnosticDataType"] = "OEM";
2734             logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2735             logEntry["Created"] = std::move(timestamp);
2736 
2737             // If logEntryJson references an array of LogEntry resources
2738             // ('Members' list), then push this as a new entry, otherwise set it
2739             // directly
2740             if (logEntryJson.is_array())
2741             {
2742                 logEntryJson.push_back(logEntry);
2743                 asyncResp->res.jsonValue["Members@odata.count"] =
2744                     logEntryJson.size();
2745             }
2746             else
2747             {
2748                 logEntryJson.update(logEntry);
2749             }
2750         };
2751     dbus::utility::getAllProperties(
2752         crashdumpObject, crashdumpPath + std::string("/") + logID,
2753         crashdumpInterface, std::move(getStoredLogCallback));
2754 }
2755 
2756 inline void requestRoutesCrashdumpEntryCollection(App& app)
2757 {
2758     // Note: Deviated from redfish privilege registry for GET & HEAD
2759     // method for security reasons.
2760     /**
2761      * Functions triggers appropriate requests on DBus
2762      */
2763     BMCWEB_ROUTE(app,
2764                  "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/")
2765         // This is incorrect, should be.
2766         //.privileges(redfish::privileges::postLogEntryCollection)
2767         .privileges({{"ConfigureComponents"}})
2768         .methods(
2769             boost::beast::http::verb::
2770                 get)([&app](const crow::Request& req,
2771                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2772                             const std::string& systemName) {
2773             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2774             {
2775                 return;
2776             }
2777             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2778             {
2779                 // Option currently returns no systems.  TBD
2780                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2781                                            systemName);
2782                 return;
2783             }
2784             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2785             {
2786                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2787                                            systemName);
2788                 return;
2789             }
2790 
2791             constexpr std::array<std::string_view, 1> interfaces = {
2792                 crashdumpInterface};
2793             dbus::utility::getSubTreePaths(
2794                 "/", 0, interfaces,
2795                 [asyncResp](const boost::system::error_code& ec,
2796                             const std::vector<std::string>& resp) {
2797                     if (ec)
2798                     {
2799                         if (ec.value() !=
2800                             boost::system::errc::no_such_file_or_directory)
2801                         {
2802                             BMCWEB_LOG_DEBUG("failed to get entries ec: {}",
2803                                              ec.message());
2804                             messages::internalError(asyncResp->res);
2805                             return;
2806                         }
2807                     }
2808                     asyncResp->res.jsonValue["@odata.type"] =
2809                         "#LogEntryCollection.LogEntryCollection";
2810                     asyncResp->res.jsonValue["@odata.id"] = std::format(
2811                         "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
2812                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
2813                     asyncResp->res.jsonValue["Name"] =
2814                         "Open BMC Crashdump Entries";
2815                     asyncResp->res.jsonValue["Description"] =
2816                         "Collection of Crashdump Entries";
2817                     asyncResp->res.jsonValue["Members"] =
2818                         nlohmann::json::array();
2819                     asyncResp->res.jsonValue["Members@odata.count"] = 0;
2820 
2821                     for (const std::string& path : resp)
2822                     {
2823                         const sdbusplus::message::object_path objPath(path);
2824                         // Get the log ID
2825                         std::string logID = objPath.filename();
2826                         if (logID.empty())
2827                         {
2828                             continue;
2829                         }
2830                         // Add the log entry to the array
2831                         logCrashdumpEntry(asyncResp, logID,
2832                                           asyncResp->res.jsonValue["Members"]);
2833                     }
2834                 });
2835         });
2836 }
2837 
2838 inline void requestRoutesCrashdumpEntry(App& app)
2839 {
2840     // Note: Deviated from redfish privilege registry for GET & HEAD
2841     // method for security reasons.
2842 
2843     BMCWEB_ROUTE(
2844         app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/")
2845         // this is incorrect, should be
2846         // .privileges(redfish::privileges::getLogEntry)
2847         .privileges({{"ConfigureComponents"}})
2848         .methods(boost::beast::http::verb::get)(
2849             [&app](const crow::Request& req,
2850                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2851                    const std::string& systemName, const std::string& param) {
2852                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2853                 {
2854                     return;
2855                 }
2856                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2857                 {
2858                     // Option currently returns no systems.  TBD
2859                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2860                                                systemName);
2861                     return;
2862                 }
2863                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2864                 {
2865                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2866                                                systemName);
2867                     return;
2868                 }
2869                 const std::string& logID = param;
2870                 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2871             });
2872 }
2873 
2874 inline void requestRoutesCrashdumpFile(App& app)
2875 {
2876     // Note: Deviated from redfish privilege registry for GET & HEAD
2877     // method for security reasons.
2878     BMCWEB_ROUTE(
2879         app,
2880         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/")
2881         .privileges(redfish::privileges::getLogEntry)
2882         .methods(boost::beast::http::verb::get)(
2883             [](const crow::Request& req,
2884                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2885                const std::string& systemName, const std::string& logID,
2886                const std::string& fileName) {
2887                 // Do not call getRedfishRoute here since the crashdump file is
2888                 // not a Redfish resource.
2889 
2890                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2891                 {
2892                     // Option currently returns no systems.  TBD
2893                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2894                                                systemName);
2895                     return;
2896                 }
2897                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2898                 {
2899                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2900                                                systemName);
2901                     return;
2902                 }
2903 
2904                 auto getStoredLogCallback =
2905                     [asyncResp, logID, fileName,
2906                      url(boost::urls::url(req.url()))](
2907                         const boost::system::error_code& ec,
2908                         const std::vector<std::pair<
2909                             std::string, dbus::utility::DbusVariantType>>&
2910                             resp) {
2911                         if (ec)
2912                         {
2913                             BMCWEB_LOG_DEBUG("failed to get log ec: {}",
2914                                              ec.message());
2915                             messages::internalError(asyncResp->res);
2916                             return;
2917                         }
2918 
2919                         std::string dbusFilename{};
2920                         std::string dbusTimestamp{};
2921                         std::string dbusFilepath{};
2922 
2923                         parseCrashdumpParameters(resp, dbusFilename,
2924                                                  dbusTimestamp, dbusFilepath);
2925 
2926                         if (dbusFilename.empty() || dbusTimestamp.empty() ||
2927                             dbusFilepath.empty())
2928                         {
2929                             messages::resourceNotFound(asyncResp->res,
2930                                                        "LogEntry", logID);
2931                             return;
2932                         }
2933 
2934                         // Verify the file name parameter is correct
2935                         if (fileName != dbusFilename)
2936                         {
2937                             messages::resourceNotFound(asyncResp->res,
2938                                                        "LogEntry", logID);
2939                             return;
2940                         }
2941 
2942                         if (asyncResp->res.openFile(dbusFilepath) !=
2943                             crow::OpenCode::Success)
2944                         {
2945                             messages::resourceNotFound(asyncResp->res,
2946                                                        "LogEntry", logID);
2947                             return;
2948                         }
2949 
2950                         // Configure this to be a file download when accessed
2951                         // from a browser
2952                         asyncResp->res.addHeader(
2953                             boost::beast::http::field::content_disposition,
2954                             "attachment");
2955                     };
2956                 dbus::utility::getAllProperties(
2957                     *crow::connections::systemBus, crashdumpObject,
2958                     crashdumpPath + std::string("/") + logID,
2959                     crashdumpInterface, std::move(getStoredLogCallback));
2960             });
2961 }
2962 
2963 enum class OEMDiagnosticType
2964 {
2965     onDemand,
2966     telemetry,
2967     invalid,
2968 };
2969 
2970 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr)
2971 {
2972     if (oemDiagStr == "OnDemand")
2973     {
2974         return OEMDiagnosticType::onDemand;
2975     }
2976     if (oemDiagStr == "Telemetry")
2977     {
2978         return OEMDiagnosticType::telemetry;
2979     }
2980 
2981     return OEMDiagnosticType::invalid;
2982 }
2983 
2984 inline void requestRoutesCrashdumpCollect(App& app)
2985 {
2986     // Note: Deviated from redfish privilege registry for GET & HEAD
2987     // method for security reasons.
2988     BMCWEB_ROUTE(
2989         app,
2990         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
2991         // The below is incorrect;  Should be ConfigureManager
2992         //.privileges(redfish::privileges::postLogService)
2993         .privileges({{"ConfigureComponents"}})
2994         .methods(boost::beast::http::verb::post)(
2995             [&app](const crow::Request& req,
2996                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2997                    const std::string& systemName) {
2998                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2999                 {
3000                     return;
3001                 }
3002 
3003                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
3004                 {
3005                     // Option currently returns no systems.  TBD
3006                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3007                                                systemName);
3008                     return;
3009                 }
3010                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
3011                 {
3012                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3013                                                systemName);
3014                     return;
3015                 }
3016 
3017                 std::string diagnosticDataType;
3018                 std::string oemDiagnosticDataType;
3019                 if (!redfish::json_util::readJsonAction(               //
3020                         req, asyncResp->res,                           //
3021                         "DiagnosticDataType", diagnosticDataType,      //
3022                         "OEMDiagnosticDataType", oemDiagnosticDataType //
3023                         ))
3024                 {
3025                     return;
3026                 }
3027 
3028                 if (diagnosticDataType != "OEM")
3029                 {
3030                     BMCWEB_LOG_ERROR(
3031                         "Only OEM DiagnosticDataType supported for Crashdump");
3032                     messages::actionParameterValueFormatError(
3033                         asyncResp->res, diagnosticDataType,
3034                         "DiagnosticDataType", "CollectDiagnosticData");
3035                     return;
3036                 }
3037 
3038                 OEMDiagnosticType oemDiagType =
3039                     getOEMDiagnosticType(oemDiagnosticDataType);
3040 
3041                 std::string iface;
3042                 std::string method;
3043                 std::string taskMatchStr;
3044                 if (oemDiagType == OEMDiagnosticType::onDemand)
3045                 {
3046                     iface = crashdumpOnDemandInterface;
3047                     method = "GenerateOnDemandLog";
3048                     taskMatchStr =
3049                         "type='signal',"
3050                         "interface='org.freedesktop.DBus.Properties',"
3051                         "member='PropertiesChanged',"
3052                         "arg0namespace='com.intel.crashdump'";
3053                 }
3054                 else if (oemDiagType == OEMDiagnosticType::telemetry)
3055                 {
3056                     iface = crashdumpTelemetryInterface;
3057                     method = "GenerateTelemetryLog";
3058                     taskMatchStr =
3059                         "type='signal',"
3060                         "interface='org.freedesktop.DBus.Properties',"
3061                         "member='PropertiesChanged',"
3062                         "arg0namespace='com.intel.crashdump'";
3063                 }
3064                 else
3065                 {
3066                     BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}",
3067                                      oemDiagnosticDataType);
3068                     messages::actionParameterValueFormatError(
3069                         asyncResp->res, oemDiagnosticDataType,
3070                         "OEMDiagnosticDataType", "CollectDiagnosticData");
3071                     return;
3072                 }
3073 
3074                 auto collectCrashdumpCallback =
3075                     [asyncResp, payload(task::Payload(req)),
3076                      taskMatchStr](const boost::system::error_code& ec,
3077                                    const std::string&) mutable {
3078                         if (ec)
3079                         {
3080                             if (ec.value() ==
3081                                 boost::system::errc::operation_not_supported)
3082                             {
3083                                 messages::resourceInStandby(asyncResp->res);
3084                             }
3085                             else if (ec.value() == boost::system::errc::
3086                                                        device_or_resource_busy)
3087                             {
3088                                 messages::serviceTemporarilyUnavailable(
3089                                     asyncResp->res, "60");
3090                             }
3091                             else
3092                             {
3093                                 messages::internalError(asyncResp->res);
3094                             }
3095                             return;
3096                         }
3097                         std::shared_ptr<task::TaskData> task =
3098                             task::TaskData::createTask(
3099                                 [](const boost::system::error_code& ec2,
3100                                    sdbusplus::message_t&,
3101                                    const std::shared_ptr<task::TaskData>&
3102                                        taskData) {
3103                                     if (!ec2)
3104                                     {
3105                                         taskData->messages.emplace_back(
3106                                             messages::taskCompletedOK(
3107                                                 std::to_string(
3108                                                     taskData->index)));
3109                                         taskData->state = "Completed";
3110                                     }
3111                                     return task::completed;
3112                                 },
3113                                 taskMatchStr);
3114 
3115                         task->startTimer(std::chrono::minutes(5));
3116                         task->populateResp(asyncResp->res);
3117                         task->payload.emplace(std::move(payload));
3118                     };
3119 
3120                 dbus::utility::async_method_call(
3121                     asyncResp, std::move(collectCrashdumpCallback),
3122                     crashdumpObject, crashdumpPath, iface, method);
3123             });
3124 }
3125 
3126 inline void dBusLogServiceActionsClear(
3127     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
3128 {
3129     BMCWEB_LOG_DEBUG("Do delete all entries.");
3130 
3131     // Process response from Logging service.
3132     auto respHandler = [asyncResp](const boost::system::error_code& ec) {
3133         BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done");
3134         if (ec)
3135         {
3136             // TODO Handle for specific error code
3137             BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec);
3138             asyncResp->res.result(
3139                 boost::beast::http::status::internal_server_error);
3140             return;
3141         }
3142 
3143         messages::success(asyncResp->res);
3144     };
3145 
3146     // Make call to Logging service to request Clear Log
3147     dbus::utility::async_method_call(
3148         asyncResp, respHandler, "xyz.openbmc_project.Logging",
3149         "/xyz/openbmc_project/logging",
3150         "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3151 }
3152 
3153 /**
3154  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3155  */
3156 inline void requestRoutesDBusLogServiceActionsClear(App& app)
3157 {
3158     /**
3159      * Function handles POST method request.
3160      * The Clear Log actions does not require any parameter.The action deletes
3161      * all entries found in the Entries collection for this Log Service.
3162      */
3163 
3164     BMCWEB_ROUTE(
3165         app,
3166         "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
3167         .privileges(redfish::privileges::postLogService)
3168         .methods(boost::beast::http::verb::post)(
3169             [&app](const crow::Request& req,
3170                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3171                    const std::string& systemName) {
3172                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3173                 {
3174                     return;
3175                 }
3176                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
3177                 {
3178                     // Option currently returns no systems.  TBD
3179                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3180                                                systemName);
3181                     return;
3182                 }
3183                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
3184                 {
3185                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3186                                                systemName);
3187                     return;
3188                 }
3189                 dBusLogServiceActionsClear(asyncResp);
3190             });
3191 }
3192 
3193 } // namespace redfish
3194