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