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