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