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