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