xref: /openbmc/bmcweb/redfish-core/lib/log_services.hpp (revision 177612aaa0633cf9d5aef0b763a43135cf552d9b)
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->populateResp(asyncResp->res);
938             task->payload.emplace(payload);
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         },
1096         "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType),
1097         "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
1098 }
1099 
parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap & params,std::string & filename,std::string & timestamp,std::string & logfile)1100 inline void parseCrashdumpParameters(
1101     const dbus::utility::DBusPropertiesMap& params, std::string& filename,
1102     std::string& timestamp, std::string& logfile)
1103 {
1104     const std::string* filenamePtr = nullptr;
1105     const std::string* timestampPtr = nullptr;
1106     const std::string* logfilePtr = nullptr;
1107 
1108     const bool success = sdbusplus::unpackPropertiesNoThrow(
1109         dbus_utils::UnpackErrorPrinter(), params, "Timestamp", timestampPtr,
1110         "Filename", filenamePtr, "Log", logfilePtr);
1111 
1112     if (!success)
1113     {
1114         return;
1115     }
1116 
1117     if (filenamePtr != nullptr)
1118     {
1119         filename = *filenamePtr;
1120     }
1121 
1122     if (timestampPtr != nullptr)
1123     {
1124         timestamp = *timestampPtr;
1125     }
1126 
1127     if (logfilePtr != nullptr)
1128     {
1129         logfile = *logfilePtr;
1130     }
1131 }
1132 
requestRoutesSystemLogServiceCollection(App & app)1133 inline void requestRoutesSystemLogServiceCollection(App& app)
1134 {
1135     /**
1136      * Functions triggers appropriate requests on DBus
1137      */
1138     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/")
1139         .privileges(redfish::privileges::getLogServiceCollection)
1140         .methods(
1141             boost::beast::http::verb::
1142                 get)([&app](const crow::Request& req,
1143                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1144                             const std::string& systemName) {
1145             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1146             {
1147                 return;
1148             }
1149             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1150             {
1151                 // Option currently returns no systems.  TBD
1152                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1153                                            systemName);
1154                 return;
1155             }
1156             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1157             {
1158                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1159                                            systemName);
1160                 return;
1161             }
1162 
1163             // Collections don't include the static data added by SubRoute
1164             // because it has a duplicate entry for members
1165             asyncResp->res.jsonValue["@odata.type"] =
1166                 "#LogServiceCollection.LogServiceCollection";
1167             asyncResp->res.jsonValue["@odata.id"] =
1168                 std::format("/redfish/v1/Systems/{}/LogServices",
1169                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
1170             asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
1171             asyncResp->res.jsonValue["Description"] =
1172                 "Collection of LogServices for this Computer System";
1173             nlohmann::json& logServiceArray =
1174                 asyncResp->res.jsonValue["Members"];
1175             logServiceArray = nlohmann::json::array();
1176             nlohmann::json::object_t eventLog;
1177             eventLog["@odata.id"] =
1178                 std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
1179                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
1180             logServiceArray.emplace_back(std::move(eventLog));
1181             if constexpr (BMCWEB_REDFISH_DUMP_LOG)
1182             {
1183                 nlohmann::json::object_t dumpLog;
1184                 dumpLog["@odata.id"] =
1185                     std::format("/redfish/v1/Systems/{}/LogServices/Dump",
1186                                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1187                 logServiceArray.emplace_back(std::move(dumpLog));
1188             }
1189 
1190             if constexpr (BMCWEB_REDFISH_CPU_LOG)
1191             {
1192                 nlohmann::json::object_t crashdump;
1193                 crashdump["@odata.id"] =
1194                     std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
1195                                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1196                 logServiceArray.emplace_back(std::move(crashdump));
1197             }
1198 
1199             if constexpr (BMCWEB_REDFISH_HOST_LOGGER)
1200             {
1201                 nlohmann::json::object_t hostlogger;
1202                 hostlogger["@odata.id"] =
1203                     std::format("/redfish/v1/Systems/{}/LogServices/HostLogger",
1204                                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1205                 logServiceArray.emplace_back(std::move(hostlogger));
1206             }
1207             asyncResp->res.jsonValue["Members@odata.count"] =
1208                 logServiceArray.size();
1209 
1210             constexpr std::array<std::string_view, 1> interfaces = {
1211                 "xyz.openbmc_project.State.Boot.PostCode"};
1212             dbus::utility::getSubTreePaths(
1213                 "/", 0, interfaces,
1214                 [asyncResp](const boost::system::error_code& ec,
1215                             const dbus::utility::MapperGetSubTreePathsResponse&
1216                                 subtreePath) {
1217                     if (ec)
1218                     {
1219                         BMCWEB_LOG_ERROR("{}", ec);
1220                         return;
1221                     }
1222 
1223                     for (const auto& pathStr : subtreePath)
1224                     {
1225                         if (pathStr.find("PostCode") != std::string::npos)
1226                         {
1227                             nlohmann::json& logServiceArrayLocal =
1228                                 asyncResp->res.jsonValue["Members"];
1229                             nlohmann::json::object_t member;
1230                             member["@odata.id"] = std::format(
1231                                 "/redfish/v1/Systems/{}/LogServices/PostCodes",
1232                                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1233 
1234                             logServiceArrayLocal.emplace_back(
1235                                 std::move(member));
1236 
1237                             asyncResp->res.jsonValue["Members@odata.count"] =
1238                                 logServiceArrayLocal.size();
1239                             return;
1240                         }
1241                     }
1242                 });
1243         });
1244 }
1245 
requestRoutesEventLogService(App & app)1246 inline void requestRoutesEventLogService(App& app)
1247 {
1248     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/")
1249         .privileges(redfish::privileges::getLogService)
1250         .methods(
1251             boost::beast::http::verb::
1252                 get)([&app](const crow::Request& req,
1253                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1254                             const std::string& systemName) {
1255             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1256             {
1257                 return;
1258             }
1259             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1260             {
1261                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1262                                            systemName);
1263                 return;
1264             }
1265             asyncResp->res.jsonValue["@odata.id"] =
1266                 std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
1267                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
1268             asyncResp->res.jsonValue["@odata.type"] =
1269                 "#LogService.v1_2_0.LogService";
1270             asyncResp->res.jsonValue["Name"] = "Event Log Service";
1271             asyncResp->res.jsonValue["Description"] =
1272                 "System Event Log Service";
1273             asyncResp->res.jsonValue["Id"] = "EventLog";
1274             asyncResp->res.jsonValue["OverWritePolicy"] =
1275                 log_service::OverWritePolicy::WrapsWhenFull;
1276 
1277             std::pair<std::string, std::string> redfishDateTimeOffset =
1278                 redfish::time_utils::getDateTimeOffsetNow();
1279 
1280             asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1281             asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1282                 redfishDateTimeOffset.second;
1283 
1284             asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
1285                 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1286                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1287             asyncResp->res
1288                 .jsonValue["Actions"]["#LogService.ClearLog"]["target"]
1289 
1290                 = std::format(
1291                     "/redfish/v1/Systems/{}/LogServices/EventLog/Actions/LogService.ClearLog",
1292                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
1293         });
1294 }
1295 
handleSystemsLogServicesEventLogActionsClearPost(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1296 inline void handleSystemsLogServicesEventLogActionsClearPost(
1297     App& app, const crow::Request& req,
1298     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1299     const std::string& systemName)
1300 {
1301     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1302     {
1303         return;
1304     }
1305     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1306     {
1307         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1308                                    systemName);
1309         return;
1310     }
1311 
1312     // Clear the EventLog by deleting the log files
1313     std::vector<std::filesystem::path> redfishLogFiles;
1314     if (getRedfishLogFiles(redfishLogFiles))
1315     {
1316         for (const std::filesystem::path& file : redfishLogFiles)
1317         {
1318             std::error_code ec;
1319             std::filesystem::remove(file, ec);
1320         }
1321     }
1322 
1323     // Reload rsyslog so it knows to start new log files
1324     dbus::utility::async_method_call(
1325         asyncResp,
1326         [asyncResp](const boost::system::error_code& ec) {
1327             if (ec)
1328             {
1329                 BMCWEB_LOG_ERROR("Failed to reload rsyslog: {}", ec);
1330                 messages::internalError(asyncResp->res);
1331                 return;
1332             }
1333 
1334             messages::success(asyncResp->res);
1335         },
1336         "org.freedesktop.systemd1", "/org/freedesktop/systemd1",
1337         "org.freedesktop.systemd1.Manager", "ReloadUnit", "rsyslog.service",
1338         "replace");
1339 }
1340 
requestRoutesJournalEventLogClear(App & app)1341 inline void requestRoutesJournalEventLogClear(App& app)
1342 {
1343     BMCWEB_ROUTE(
1344         app,
1345         "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
1346         .privileges({{"ConfigureComponents"}})
1347         .methods(boost::beast::http::verb::post)(std::bind_front(
1348             handleSystemsLogServicesEventLogActionsClearPost, std::ref(app)));
1349 }
1350 
1351 enum class LogParseError
1352 {
1353     success,
1354     parseFailed,
1355     messageIdNotInRegistry,
1356 };
1357 
fillEventLogEntryJson(const std::string & logEntryID,const std::string & logEntry,nlohmann::json::object_t & logEntryJson)1358 static LogParseError fillEventLogEntryJson(
1359     const std::string& logEntryID, const std::string& logEntry,
1360     nlohmann::json::object_t& logEntryJson)
1361 {
1362     // The redfish log format is "<Timestamp> <MessageId>,<MessageArgs>"
1363     // First get the Timestamp
1364     size_t space = logEntry.find_first_of(' ');
1365     if (space == std::string::npos)
1366     {
1367         return LogParseError::parseFailed;
1368     }
1369     std::string timestamp = logEntry.substr(0, space);
1370     // Then get the log contents
1371     size_t entryStart = logEntry.find_first_not_of(' ', space);
1372     if (entryStart == std::string::npos)
1373     {
1374         return LogParseError::parseFailed;
1375     }
1376     std::string_view entry(logEntry);
1377     entry.remove_prefix(entryStart);
1378     // Use split to separate the entry into its fields
1379     std::vector<std::string> logEntryFields;
1380     bmcweb::split(logEntryFields, entry, ',');
1381     // We need at least a MessageId to be valid
1382     auto logEntryIter = logEntryFields.begin();
1383     if (logEntryIter == logEntryFields.end())
1384     {
1385         return LogParseError::parseFailed;
1386     }
1387     std::string& messageID = *logEntryIter;
1388     // Get the Message from the MessageRegistry
1389     const registries::Message* message = registries::getMessage(messageID);
1390 
1391     logEntryIter++;
1392     if (message == nullptr)
1393     {
1394         BMCWEB_LOG_WARNING("Log entry not found in registry: {}", logEntry);
1395         return LogParseError::messageIdNotInRegistry;
1396     }
1397 
1398     std::vector<std::string_view> messageArgs(logEntryIter,
1399                                               logEntryFields.end());
1400     messageArgs.resize(message->numberOfArgs);
1401 
1402     std::string msg =
1403         redfish::registries::fillMessageArgs(messageArgs, message->message);
1404     if (msg.empty())
1405     {
1406         return LogParseError::parseFailed;
1407     }
1408 
1409     // Get the Created time from the timestamp. The log timestamp is in RFC3339
1410     // format which matches the Redfish format except for the fractional seconds
1411     // between the '.' and the '+', so just remove them.
1412     std::size_t dot = timestamp.find_first_of('.');
1413     std::size_t plus = timestamp.find_first_of('+');
1414     if (dot != std::string::npos && plus != std::string::npos)
1415     {
1416         timestamp.erase(dot, plus - dot);
1417     }
1418 
1419     // Fill in the log entry with the gathered data
1420     logEntryJson["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1421     logEntryJson["@odata.id"] = boost::urls::format(
1422         "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}",
1423         BMCWEB_REDFISH_SYSTEM_URI_NAME, logEntryID);
1424     logEntryJson["Name"] = "System Event Log Entry";
1425     logEntryJson["Id"] = logEntryID;
1426     logEntryJson["Message"] = std::move(msg);
1427     logEntryJson["MessageId"] = std::move(messageID);
1428     logEntryJson["MessageArgs"] = messageArgs;
1429     logEntryJson["EntryType"] = "Event";
1430     logEntryJson["Severity"] = message->messageSeverity;
1431     logEntryJson["Created"] = std::move(timestamp);
1432     return LogParseError::success;
1433 }
1434 
fillEventLogLogEntryFromPropertyMap(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const dbus::utility::DBusPropertiesMap & resp,nlohmann::json & objectToFillOut)1435 inline bool fillEventLogLogEntryFromPropertyMap(
1436     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1437     const dbus::utility::DBusPropertiesMap& resp,
1438     nlohmann::json& objectToFillOut)
1439 {
1440     std::optional<DbusEventLogEntry> optEntry =
1441         fillDbusEventLogEntryFromPropertyMap(resp);
1442 
1443     if (!optEntry.has_value())
1444     {
1445         messages::internalError(asyncResp->res);
1446         return false;
1447     }
1448     DbusEventLogEntry entry = optEntry.value();
1449 
1450     objectToFillOut["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1451     objectToFillOut["@odata.id"] = boost::urls::format(
1452         "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}",
1453         BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id));
1454     objectToFillOut["Name"] = "System Event Log Entry";
1455     objectToFillOut["Id"] = std::to_string(entry.Id);
1456     objectToFillOut["Message"] = entry.Message;
1457     objectToFillOut["Resolved"] = entry.Resolved;
1458     std::optional<bool> notifyAction =
1459         getProviderNotifyAction(entry.ServiceProviderNotify);
1460     if (notifyAction)
1461     {
1462         objectToFillOut["ServiceProviderNotified"] = *notifyAction;
1463     }
1464     if ((entry.Resolution != nullptr) && !entry.Resolution->empty())
1465     {
1466         objectToFillOut["Resolution"] = *entry.Resolution;
1467     }
1468     objectToFillOut["EntryType"] = "Event";
1469     objectToFillOut["Severity"] =
1470         translateSeverityDbusToRedfish(entry.Severity);
1471     objectToFillOut["Created"] =
1472         redfish::time_utils::getDateTimeUintMs(entry.Timestamp);
1473     objectToFillOut["Modified"] =
1474         redfish::time_utils::getDateTimeUintMs(entry.UpdateTimestamp);
1475     if (entry.Path != nullptr)
1476     {
1477         objectToFillOut["AdditionalDataURI"] = boost::urls::format(
1478             "/redfish/v1/Systems/{}/LogServices/EventLog/Entries/{}/attachment",
1479             BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(entry.Id));
1480     }
1481     return true;
1482 }
1483 
afterLogEntriesGetManagedObjects(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const boost::system::error_code & ec,const dbus::utility::ManagedObjectType & resp)1484 inline void afterLogEntriesGetManagedObjects(
1485     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1486     const boost::system::error_code& ec,
1487     const dbus::utility::ManagedObjectType& resp)
1488 {
1489     if (ec)
1490     {
1491         // TODO Handle for specific error code
1492         BMCWEB_LOG_ERROR("getLogEntriesIfaceData resp_handler got error {}",
1493                          ec);
1494         messages::internalError(asyncResp->res);
1495         return;
1496     }
1497     nlohmann::json::array_t entriesArray;
1498     for (const auto& objectPath : resp)
1499     {
1500         dbus::utility::DBusPropertiesMap propsFlattened;
1501         auto isEntry =
1502             std::ranges::find_if(objectPath.second, [](const auto& object) {
1503                 return object.first == "xyz.openbmc_project.Logging.Entry";
1504             });
1505         if (isEntry == objectPath.second.end())
1506         {
1507             continue;
1508         }
1509         for (const auto& interfaceMap : objectPath.second)
1510         {
1511             for (const auto& propertyMap : interfaceMap.second)
1512             {
1513                 propsFlattened.emplace_back(propertyMap.first,
1514                                             propertyMap.second);
1515             }
1516         }
1517         bool success = fillEventLogLogEntryFromPropertyMap(
1518             asyncResp, propsFlattened, entriesArray.emplace_back());
1519         if (!success)
1520         {
1521             return;
1522         }
1523     }
1524 
1525     redfish::json_util::sortJsonArrayByKey(entriesArray, "Id");
1526     asyncResp->res.jsonValue["Members@odata.count"] = entriesArray.size();
1527     asyncResp->res.jsonValue["Members"] = std::move(entriesArray);
1528 }
1529 
handleSystemsLogServiceEventLogLogEntryCollection(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1530 inline void handleSystemsLogServiceEventLogLogEntryCollection(
1531     App& app, const crow::Request& req,
1532     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1533     const std::string& systemName)
1534 {
1535     query_param::QueryCapabilities capabilities = {
1536         .canDelegateTop = true,
1537         .canDelegateSkip = true,
1538     };
1539     query_param::Query delegatedQuery;
1540     if (!redfish::setUpRedfishRouteWithDelegation(app, req, asyncResp,
1541                                                   delegatedQuery, capabilities))
1542     {
1543         return;
1544     }
1545     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1546     {
1547         // Option currently returns no systems.  TBD
1548         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1549                                    systemName);
1550         return;
1551     }
1552     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1553     {
1554         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1555                                    systemName);
1556         return;
1557     }
1558 
1559     size_t top = delegatedQuery.top.value_or(query_param::Query::maxTop);
1560     size_t skip = delegatedQuery.skip.value_or(0);
1561 
1562     // Collections don't include the static data added by SubRoute
1563     // because it has a duplicate entry for members
1564     asyncResp->res.jsonValue["@odata.type"] =
1565         "#LogEntryCollection.LogEntryCollection";
1566     asyncResp->res.jsonValue["@odata.id"] =
1567         std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1568                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
1569     asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1570     asyncResp->res.jsonValue["Description"] =
1571         "Collection of System Event Log Entries";
1572 
1573     nlohmann::json& logEntryArray = asyncResp->res.jsonValue["Members"];
1574     logEntryArray = nlohmann::json::array();
1575     // Go through the log files and create a unique ID for each
1576     // entry
1577     std::vector<std::filesystem::path> redfishLogFiles;
1578     getRedfishLogFiles(redfishLogFiles);
1579     uint64_t entryCount = 0;
1580     std::string logEntry;
1581 
1582     // Oldest logs are in the last file, so start there and loop
1583     // backwards
1584     for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
1585     {
1586         std::ifstream logStream(*it);
1587         if (!logStream.is_open())
1588         {
1589             continue;
1590         }
1591 
1592         // Reset the unique ID on the first entry
1593         bool firstEntry = true;
1594         while (std::getline(logStream, logEntry))
1595         {
1596             std::string idStr;
1597             if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1598             {
1599                 continue;
1600             }
1601             firstEntry = false;
1602 
1603             nlohmann::json::object_t bmcLogEntry;
1604             LogParseError status =
1605                 fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1606             if (status == LogParseError::messageIdNotInRegistry)
1607             {
1608                 continue;
1609             }
1610             if (status != LogParseError::success)
1611             {
1612                 messages::internalError(asyncResp->res);
1613                 return;
1614             }
1615 
1616             entryCount++;
1617             // Handle paging using skip (number of entries to skip from the
1618             // start) and top (number of entries to display)
1619             if (entryCount <= skip || entryCount > skip + top)
1620             {
1621                 continue;
1622             }
1623 
1624             logEntryArray.emplace_back(std::move(bmcLogEntry));
1625         }
1626     }
1627     asyncResp->res.jsonValue["Members@odata.count"] = entryCount;
1628     if (skip + top < entryCount)
1629     {
1630         asyncResp->res.jsonValue["Members@odata.nextLink"] =
1631             boost::urls::format(
1632                 "/redfish/v1/Systems/{}/LogServices/EventLog/Entries?$skip={}",
1633                 BMCWEB_REDFISH_SYSTEM_URI_NAME, std::to_string(skip + top));
1634     }
1635 }
1636 
requestRoutesJournalEventLogEntryCollection(App & app)1637 inline void requestRoutesJournalEventLogEntryCollection(App& app)
1638 {
1639     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1640         .privileges(redfish::privileges::getLogEntryCollection)
1641         .methods(boost::beast::http::verb::get)(std::bind_front(
1642             handleSystemsLogServiceEventLogLogEntryCollection, std::ref(app)));
1643 }
1644 
handleSystemsLogServiceEventLogEntriesGet(App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName,const std::string & param)1645 inline void handleSystemsLogServiceEventLogEntriesGet(
1646     App& app, const crow::Request& req,
1647     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1648     const std::string& systemName, const std::string& param)
1649 {
1650     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1651     {
1652         return;
1653     }
1654     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1655     {
1656         // Option currently returns no systems.  TBD
1657         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1658                                    systemName);
1659         return;
1660     }
1661 
1662     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1663     {
1664         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1665                                    systemName);
1666         return;
1667     }
1668 
1669     const std::string& targetID = param;
1670 
1671     // Go through the log files and check the unique ID for each
1672     // entry to find the target entry
1673     std::vector<std::filesystem::path> redfishLogFiles;
1674     getRedfishLogFiles(redfishLogFiles);
1675     std::string logEntry;
1676 
1677     // Oldest logs are in the last file, so start there and loop
1678     // backwards
1679     for (auto it = redfishLogFiles.rbegin(); it < redfishLogFiles.rend(); it++)
1680     {
1681         std::ifstream logStream(*it);
1682         if (!logStream.is_open())
1683         {
1684             continue;
1685         }
1686 
1687         // Reset the unique ID on the first entry
1688         bool firstEntry = true;
1689         while (std::getline(logStream, logEntry))
1690         {
1691             std::string idStr;
1692             if (!getUniqueEntryID(logEntry, idStr, firstEntry))
1693             {
1694                 continue;
1695             }
1696             firstEntry = false;
1697 
1698             if (idStr == targetID)
1699             {
1700                 nlohmann::json::object_t bmcLogEntry;
1701                 LogParseError status =
1702                     fillEventLogEntryJson(idStr, logEntry, bmcLogEntry);
1703                 if (status != LogParseError::success)
1704                 {
1705                     messages::internalError(asyncResp->res);
1706                     return;
1707                 }
1708                 asyncResp->res.jsonValue.update(bmcLogEntry);
1709                 return;
1710             }
1711         }
1712     }
1713     // Requested ID was not found
1714     messages::resourceNotFound(asyncResp->res, "LogEntry", targetID);
1715 }
1716 
requestRoutesJournalEventLogEntry(App & app)1717 inline void requestRoutesJournalEventLogEntry(App& app)
1718 {
1719     BMCWEB_ROUTE(
1720         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1721         .privileges(redfish::privileges::getLogEntry)
1722         .methods(boost::beast::http::verb::get)(std::bind_front(
1723             handleSystemsLogServiceEventLogEntriesGet, std::ref(app)));
1724 }
1725 
dBusEventLogEntryCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)1726 inline void dBusEventLogEntryCollection(
1727     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
1728 {
1729     // Collections don't include the static data added by SubRoute
1730     // because it has a duplicate entry for members
1731     asyncResp->res.jsonValue["@odata.type"] =
1732         "#LogEntryCollection.LogEntryCollection";
1733     asyncResp->res.jsonValue["@odata.id"] =
1734         std::format("/redfish/v1/Systems/{}/LogServices/EventLog/Entries",
1735                     BMCWEB_REDFISH_SYSTEM_URI_NAME);
1736     asyncResp->res.jsonValue["Name"] = "System Event Log Entries";
1737     asyncResp->res.jsonValue["Description"] =
1738         "Collection of System Event Log Entries";
1739 
1740     // DBus implementation of EventLog/Entries
1741     // Make call to Logging Service to find all log entry objects
1742     sdbusplus::message::object_path path("/xyz/openbmc_project/logging");
1743     dbus::utility::getManagedObjects(
1744         "xyz.openbmc_project.Logging", path,
1745         [asyncResp](const boost::system::error_code& ec,
1746                     const dbus::utility::ManagedObjectType& resp) {
1747             afterLogEntriesGetManagedObjects(asyncResp, ec, resp);
1748         });
1749 }
1750 
requestRoutesDBusEventLogEntryCollection(App & app)1751 inline void requestRoutesDBusEventLogEntryCollection(App& app)
1752 {
1753     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/")
1754         .privileges(redfish::privileges::getLogEntryCollection)
1755         .methods(boost::beast::http::verb::get)(
1756             [&app](const crow::Request& req,
1757                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1758                    const std::string& systemName) {
1759                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1760                 {
1761                     return;
1762                 }
1763                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1764                 {
1765                     // Option currently returns no systems.  TBD
1766                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1767                                                systemName);
1768                     return;
1769                 }
1770                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1771                 {
1772                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1773                                                systemName);
1774                     return;
1775                 }
1776                 dBusEventLogEntryCollection(asyncResp);
1777             });
1778 }
1779 
dBusEventLogEntryGet(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string entryID)1780 inline void dBusEventLogEntryGet(
1781     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
1782 {
1783     dbus::utility::escapePathForDbus(entryID);
1784 
1785     // DBus implementation of EventLog/Entries
1786     // Make call to Logging Service to find all log entry objects
1787     dbus::utility::getAllProperties(
1788         "xyz.openbmc_project.Logging",
1789         "/xyz/openbmc_project/logging/entry/" + entryID, "",
1790         [asyncResp, entryID](const boost::system::error_code& ec,
1791                              const dbus::utility::DBusPropertiesMap& resp) {
1792             if (ec.value() == EBADR)
1793             {
1794                 messages::resourceNotFound(asyncResp->res, "EventLogEntry",
1795                                            entryID);
1796                 return;
1797             }
1798             if (ec)
1799             {
1800                 BMCWEB_LOG_ERROR(
1801                     "EventLogEntry (DBus) resp_handler got error {}", ec);
1802                 messages::internalError(asyncResp->res);
1803                 return;
1804             }
1805 
1806             fillEventLogLogEntryFromPropertyMap(asyncResp, resp,
1807                                                 asyncResp->res.jsonValue);
1808         });
1809 }
1810 
dBusEventLogEntryPatch(const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryId)1811 inline void dBusEventLogEntryPatch(
1812     const crow::Request& req,
1813     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1814     const std::string& entryId)
1815 {
1816     std::optional<bool> resolved;
1817 
1818     if (!json_util::readJsonPatch(req, asyncResp->res, "Resolved", resolved))
1819     {
1820         return;
1821     }
1822     BMCWEB_LOG_DEBUG("Set Resolved");
1823 
1824     setDbusProperty(asyncResp, "Resolved", "xyz.openbmc_project.Logging",
1825                     "/xyz/openbmc_project/logging/entry/" + entryId,
1826                     "xyz.openbmc_project.Logging.Entry", "Resolved",
1827                     resolved.value_or(false));
1828 }
1829 
dBusEventLogEntryDelete(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,std::string entryID)1830 inline void dBusEventLogEntryDelete(
1831     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, std::string entryID)
1832 {
1833     BMCWEB_LOG_DEBUG("Do delete single event entries.");
1834 
1835     dbus::utility::escapePathForDbus(entryID);
1836 
1837     // Process response from Logging service.
1838     auto respHandler = [asyncResp,
1839                         entryID](const boost::system::error_code& ec) {
1840         BMCWEB_LOG_DEBUG("EventLogEntry (DBus) doDelete callback: Done");
1841         if (ec)
1842         {
1843             if (ec.value() == EBADR)
1844             {
1845                 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
1846                 return;
1847             }
1848             // TODO Handle for specific error code
1849             BMCWEB_LOG_ERROR(
1850                 "EventLogEntry (DBus) doDelete respHandler got error {}", ec);
1851             asyncResp->res.result(
1852                 boost::beast::http::status::internal_server_error);
1853             return;
1854         }
1855 
1856         asyncResp->res.result(boost::beast::http::status::ok);
1857     };
1858 
1859     // Make call to Logging service to request Delete Log
1860     dbus::utility::async_method_call(
1861         asyncResp, respHandler, "xyz.openbmc_project.Logging",
1862         "/xyz/openbmc_project/logging/entry/" + entryID,
1863         "xyz.openbmc_project.Object.Delete", "Delete");
1864 }
1865 
requestRoutesDBusEventLogEntry(App & app)1866 inline void requestRoutesDBusEventLogEntry(App& app)
1867 {
1868     BMCWEB_ROUTE(
1869         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1870         .privileges(redfish::privileges::getLogEntry)
1871         .methods(boost::beast::http::verb::get)(
1872             [&app](const crow::Request& req,
1873                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1874                    const std::string& systemName, const std::string& entryId) {
1875                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1876                 {
1877                     return;
1878                 }
1879                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1880                 {
1881                     // Option currently returns no systems.  TBD
1882                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1883                                                systemName);
1884                     return;
1885                 }
1886                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1887                 {
1888                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1889                                                systemName);
1890                     return;
1891                 }
1892 
1893                 dBusEventLogEntryGet(asyncResp, entryId);
1894             });
1895 
1896     BMCWEB_ROUTE(
1897         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1898         .privileges(redfish::privileges::patchLogEntry)
1899         .methods(boost::beast::http::verb::patch)(
1900             [&app](const crow::Request& req,
1901                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1902                    const std::string& systemName, const std::string& entryId) {
1903                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1904                 {
1905                     return;
1906                 }
1907                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1908                 {
1909                     // Option currently returns no systems.  TBD
1910                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1911                                                systemName);
1912                     return;
1913                 }
1914                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1915                 {
1916                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1917                                                systemName);
1918                     return;
1919                 }
1920 
1921                 dBusEventLogEntryPatch(req, asyncResp, entryId);
1922             });
1923 
1924     BMCWEB_ROUTE(
1925         app, "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/")
1926         .privileges(redfish::privileges::deleteLogEntry)
1927 
1928         .methods(boost::beast::http::verb::delete_)(
1929             [&app](const crow::Request& req,
1930                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1931                    const std::string& systemName, const std::string& param) {
1932                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1933                 {
1934                     return;
1935                 }
1936                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1937                 {
1938                     // Option currently returns no systems.  TBD
1939                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1940                                                systemName);
1941                     return;
1942                 }
1943                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1944                 {
1945                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1946                                                systemName);
1947                     return;
1948                 }
1949                 dBusEventLogEntryDelete(asyncResp, param);
1950             });
1951 }
1952 
handleBMCLogServicesCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1953 inline void handleBMCLogServicesCollectionGet(
1954     crow::App& app, const crow::Request& req,
1955     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1956     const std::string& managerId)
1957 {
1958     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1959     {
1960         return;
1961     }
1962 
1963     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1964     {
1965         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1966         return;
1967     }
1968 
1969     // Collections don't include the static data added by SubRoute
1970     // because it has a duplicate entry for members
1971     asyncResp->res.jsonValue["@odata.type"] =
1972         "#LogServiceCollection.LogServiceCollection";
1973     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1974         "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME);
1975     asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1976     asyncResp->res.jsonValue["Description"] =
1977         "Collection of LogServices for this Manager";
1978     nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
1979     logServiceArray = nlohmann::json::array();
1980 
1981     if constexpr (BMCWEB_REDFISH_BMC_JOURNAL)
1982     {
1983         nlohmann::json::object_t journal;
1984         journal["@odata.id"] =
1985             boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal",
1986                                 BMCWEB_REDFISH_MANAGER_URI_NAME);
1987         logServiceArray.emplace_back(std::move(journal));
1988     }
1989 
1990     asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
1991 
1992     if constexpr (BMCWEB_REDFISH_DUMP_LOG)
1993     {
1994         constexpr std::array<std::string_view, 1> interfaces = {
1995             "xyz.openbmc_project.Collection.DeleteAll"};
1996         dbus::utility::getSubTreePaths(
1997             "/xyz/openbmc_project/dump", 0, interfaces,
1998             [asyncResp](const boost::system::error_code& ec,
1999                         const dbus::utility::MapperGetSubTreePathsResponse&
2000                             subTreePaths) {
2001                 if (ec)
2002                 {
2003                     BMCWEB_LOG_ERROR(
2004                         "handleBMCLogServicesCollectionGet respHandler got error {}",
2005                         ec);
2006                     // Assume that getting an error simply means there are no
2007                     // dump LogServices. Return without adding any error
2008                     // response.
2009                     return;
2010                 }
2011 
2012                 nlohmann::json& logServiceArrayLocal =
2013                     asyncResp->res.jsonValue["Members"];
2014 
2015                 for (const std::string& path : subTreePaths)
2016                 {
2017                     if (path == "/xyz/openbmc_project/dump/bmc")
2018                     {
2019                         nlohmann::json::object_t member;
2020                         member["@odata.id"] = boost::urls::format(
2021                             "/redfish/v1/Managers/{}/LogServices/Dump",
2022                             BMCWEB_REDFISH_MANAGER_URI_NAME);
2023                         logServiceArrayLocal.emplace_back(std::move(member));
2024                     }
2025                     else if (path == "/xyz/openbmc_project/dump/faultlog")
2026                     {
2027                         nlohmann::json::object_t member;
2028                         member["@odata.id"] = boost::urls::format(
2029                             "/redfish/v1/Managers/{}/LogServices/FaultLog",
2030                             BMCWEB_REDFISH_MANAGER_URI_NAME);
2031                         logServiceArrayLocal.emplace_back(std::move(member));
2032                     }
2033                 }
2034 
2035                 asyncResp->res.jsonValue["Members@odata.count"] =
2036                     logServiceArrayLocal.size();
2037             });
2038     }
2039 }
2040 
requestRoutesBMCLogServiceCollection(App & app)2041 inline void requestRoutesBMCLogServiceCollection(App& app)
2042 {
2043     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/")
2044         .privileges(redfish::privileges::getLogServiceCollection)
2045         .methods(boost::beast::http::verb::get)(
2046             std::bind_front(handleBMCLogServicesCollectionGet, std::ref(app)));
2047 }
2048 
getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)2049 inline void getDumpServiceInfo(
2050     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2051     const std::string& dumpType)
2052 {
2053     std::string dumpPath;
2054     log_service::OverWritePolicy overWritePolicy =
2055         log_service::OverWritePolicy::Invalid;
2056     bool collectDiagnosticDataSupported = false;
2057 
2058     if (dumpType == "BMC")
2059     {
2060         dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump",
2061                                BMCWEB_REDFISH_MANAGER_URI_NAME);
2062         overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
2063         collectDiagnosticDataSupported = true;
2064     }
2065     else if (dumpType == "FaultLog")
2066     {
2067         dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog",
2068                                BMCWEB_REDFISH_MANAGER_URI_NAME);
2069         overWritePolicy = log_service::OverWritePolicy::Unknown;
2070         collectDiagnosticDataSupported = false;
2071     }
2072     else if (dumpType == "System")
2073     {
2074         dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump",
2075                                BMCWEB_REDFISH_SYSTEM_URI_NAME);
2076         overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
2077         collectDiagnosticDataSupported = true;
2078     }
2079     else
2080     {
2081         BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}",
2082                          dumpType);
2083         messages::internalError(asyncResp->res);
2084         return;
2085     }
2086 
2087     asyncResp->res.jsonValue["@odata.id"] = dumpPath;
2088     asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
2089     asyncResp->res.jsonValue["Name"] = "Dump LogService";
2090     asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
2091     asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
2092     asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy;
2093 
2094     std::pair<std::string, std::string> redfishDateTimeOffset =
2095         redfish::time_utils::getDateTimeOffsetNow();
2096     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2097     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2098         redfishDateTimeOffset.second;
2099 
2100     asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
2101 
2102     if (collectDiagnosticDataSupported)
2103     {
2104         asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2105                                 ["target"] =
2106             dumpPath + "/Actions/LogService.CollectDiagnosticData";
2107     }
2108 
2109     constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface};
2110     dbus::utility::getSubTreePaths(
2111         "/xyz/openbmc_project/dump", 0, interfaces,
2112         [asyncResp, dumpType, dumpPath](
2113             const boost::system::error_code& ec,
2114             const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
2115             if (ec)
2116             {
2117                 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}",
2118                                  ec);
2119                 // Assume that getting an error simply means there are no dump
2120                 // LogServices. Return without adding any error response.
2121                 return;
2122             }
2123             std::string dbusDumpPath = getDumpPath(dumpType);
2124             for (const std::string& path : subTreePaths)
2125             {
2126                 if (path == dbusDumpPath)
2127                 {
2128                     asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
2129                                             ["target"] =
2130                         dumpPath + "/Actions/LogService.ClearLog";
2131                     break;
2132                 }
2133             }
2134         });
2135 }
2136 
handleLogServicesDumpServiceGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2137 inline void handleLogServicesDumpServiceGet(
2138     crow::App& app, const std::string& dumpType, const crow::Request& req,
2139     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2140     const std::string& managerId)
2141 {
2142     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2143     {
2144         return;
2145     }
2146 
2147     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2148     {
2149         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2150         return;
2151     }
2152 
2153     getDumpServiceInfo(asyncResp, dumpType);
2154 }
2155 
handleLogServicesDumpServiceComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)2156 inline void handleLogServicesDumpServiceComputerSystemGet(
2157     crow::App& app, const crow::Request& req,
2158     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2159     const std::string& chassisId)
2160 {
2161     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2162     {
2163         return;
2164     }
2165     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2166     {
2167         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2168         return;
2169     }
2170     getDumpServiceInfo(asyncResp, "System");
2171 }
2172 
handleLogServicesDumpEntriesCollectionGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2173 inline void handleLogServicesDumpEntriesCollectionGet(
2174     crow::App& app, const std::string& dumpType, const crow::Request& req,
2175     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2176     const std::string& managerId)
2177 {
2178     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2179     {
2180         return;
2181     }
2182 
2183     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2184     {
2185         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2186         return;
2187     }
2188     getDumpEntryCollection(asyncResp, dumpType);
2189 }
2190 
handleLogServicesDumpEntriesCollectionComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)2191 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet(
2192     crow::App& app, const crow::Request& req,
2193     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2194     const std::string& chassisId)
2195 {
2196     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2197     {
2198         return;
2199     }
2200     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2201     {
2202         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2203         return;
2204     }
2205     getDumpEntryCollection(asyncResp, "System");
2206 }
2207 
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)2208 inline void handleLogServicesDumpEntryGet(
2209     crow::App& app, const std::string& dumpType, const crow::Request& req,
2210     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2211     const std::string& managerId, const std::string& dumpId)
2212 {
2213     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2214     {
2215         return;
2216     }
2217     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2218     {
2219         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2220         return;
2221     }
2222     getDumpEntryById(asyncResp, dumpId, dumpType);
2223 }
2224 
handleLogServicesDumpEntryComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & dumpId)2225 inline void handleLogServicesDumpEntryComputerSystemGet(
2226     crow::App& app, const crow::Request& req,
2227     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2228     const std::string& chassisId, const std::string& dumpId)
2229 {
2230     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2231     {
2232         return;
2233     }
2234     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2235     {
2236         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2237         return;
2238     }
2239     getDumpEntryById(asyncResp, dumpId, "System");
2240 }
2241 
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)2242 inline void handleLogServicesDumpEntryDelete(
2243     crow::App& app, const std::string& dumpType, const crow::Request& req,
2244     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2245     const std::string& managerId, const std::string& dumpId)
2246 {
2247     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2248     {
2249         return;
2250     }
2251 
2252     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2253     {
2254         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2255         return;
2256     }
2257     deleteDumpEntry(asyncResp, dumpId, dumpType);
2258 }
2259 
handleLogServicesDumpEntryComputerSystemDelete(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & dumpId)2260 inline void handleLogServicesDumpEntryComputerSystemDelete(
2261     crow::App& app, const crow::Request& req,
2262     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2263     const std::string& chassisId, const std::string& dumpId)
2264 {
2265     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2266     {
2267         return;
2268     }
2269     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2270     {
2271         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
2272         return;
2273     }
2274     deleteDumpEntry(asyncResp, dumpId, "System");
2275 }
2276 
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)2277 inline void handleLogServicesDumpEntryDownloadGet(
2278     crow::App& app, const std::string& dumpType, const crow::Request& req,
2279     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2280     const std::string& managerId, const std::string& dumpId)
2281 {
2282     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2283     {
2284         return;
2285     }
2286 
2287     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2288     {
2289         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2290         return;
2291     }
2292     downloadDumpEntry(asyncResp, dumpId, dumpType);
2293 }
2294 
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)2295 inline void handleDBusEventLogEntryDownloadGet(
2296     crow::App& app, const std::string& dumpType, const crow::Request& req,
2297     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2298     const std::string& systemName, const std::string& entryID)
2299 {
2300     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2301     {
2302         return;
2303     }
2304     if (!http_helpers::isContentTypeAllowed(
2305             req.getHeaderValue("Accept"),
2306             http_helpers::ContentType::OctetStream, true))
2307     {
2308         asyncResp->res.result(boost::beast::http::status::bad_request);
2309         return;
2310     }
2311     downloadEventLogEntry(asyncResp, systemName, entryID, dumpType);
2312 }
2313 
handleLogServicesDumpCollectDiagnosticDataPost(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2314 inline void handleLogServicesDumpCollectDiagnosticDataPost(
2315     crow::App& app, const std::string& dumpType, const crow::Request& req,
2316     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2317     const std::string& managerId)
2318 {
2319     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2320     {
2321         return;
2322     }
2323     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2324     {
2325         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2326         return;
2327     }
2328 
2329     createDump(asyncResp, req, dumpType);
2330 }
2331 
handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)2332 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(
2333     crow::App& app, const crow::Request& req,
2334     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2335     const std::string& systemName)
2336 {
2337     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2338     {
2339         return;
2340     }
2341 
2342     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2343     {
2344         // Option currently returns no systems.  TBD
2345         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2346                                    systemName);
2347         return;
2348     }
2349     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2350     {
2351         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2352                                    systemName);
2353         return;
2354     }
2355     createDump(asyncResp, req, "System");
2356 }
2357 
handleLogServicesDumpClearLogPost(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)2358 inline void handleLogServicesDumpClearLogPost(
2359     crow::App& app, const std::string& dumpType, const crow::Request& req,
2360     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2361     const std::string& managerId)
2362 {
2363     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2364     {
2365         return;
2366     }
2367 
2368     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
2369     {
2370         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
2371         return;
2372     }
2373     clearDump(asyncResp, dumpType);
2374 }
2375 
handleLogServicesDumpClearLogComputerSystemPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)2376 inline void handleLogServicesDumpClearLogComputerSystemPost(
2377     crow::App& app, const crow::Request& req,
2378     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2379     const std::string& systemName)
2380 {
2381     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2382     {
2383         return;
2384     }
2385     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2386     {
2387         // Option currently returns no systems.  TBD
2388         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2389                                    systemName);
2390         return;
2391     }
2392     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2393     {
2394         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2395                                    systemName);
2396         return;
2397     }
2398     clearDump(asyncResp, "System");
2399 }
2400 
requestRoutesBMCDumpService(App & app)2401 inline void requestRoutesBMCDumpService(App& app)
2402 {
2403     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/")
2404         .privileges(redfish::privileges::getLogService)
2405         .methods(boost::beast::http::verb::get)(std::bind_front(
2406             handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
2407 }
2408 
requestRoutesBMCDumpEntryCollection(App & app)2409 inline void requestRoutesBMCDumpEntryCollection(App& app)
2410 {
2411     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/")
2412         .privileges(redfish::privileges::getLogEntryCollection)
2413         .methods(boost::beast::http::verb::get)(std::bind_front(
2414             handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
2415 }
2416 
requestRoutesBMCDumpEntry(App & app)2417 inline void requestRoutesBMCDumpEntry(App& app)
2418 {
2419     BMCWEB_ROUTE(app,
2420                  "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
2421         .privileges(redfish::privileges::getLogEntry)
2422         .methods(boost::beast::http::verb::get)(std::bind_front(
2423             handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
2424 
2425     BMCWEB_ROUTE(app,
2426                  "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
2427         .privileges(redfish::privileges::deleteLogEntry)
2428         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2429             handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
2430 }
2431 
requestRoutesBMCDumpEntryDownload(App & app)2432 inline void requestRoutesBMCDumpEntryDownload(App& app)
2433 {
2434     BMCWEB_ROUTE(
2435         app,
2436         "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/")
2437         .privileges(redfish::privileges::getLogEntry)
2438         .methods(boost::beast::http::verb::get)(std::bind_front(
2439             handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC"));
2440 }
2441 
requestRoutesBMCDumpCreate(App & app)2442 inline void requestRoutesBMCDumpCreate(App& app)
2443 {
2444     BMCWEB_ROUTE(
2445         app,
2446         "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2447         .privileges(redfish::privileges::postLogService)
2448         .methods(boost::beast::http::verb::post)(
2449             std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
2450                             std::ref(app), "BMC"));
2451 }
2452 
requestRoutesBMCDumpClear(App & app)2453 inline void requestRoutesBMCDumpClear(App& app)
2454 {
2455     BMCWEB_ROUTE(
2456         app,
2457         "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2458         .privileges(redfish::privileges::postLogService)
2459         .methods(boost::beast::http::verb::post)(std::bind_front(
2460             handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
2461 }
2462 
requestRoutesDBusEventLogEntryDownload(App & app)2463 inline void requestRoutesDBusEventLogEntryDownload(App& app)
2464 {
2465     BMCWEB_ROUTE(
2466         app,
2467         "/redfish/v1/Systems/<str>/LogServices/EventLog/Entries/<str>/attachment/")
2468         .privileges(redfish::privileges::getLogEntry)
2469         .methods(boost::beast::http::verb::get)(std::bind_front(
2470             handleDBusEventLogEntryDownloadGet, std::ref(app), "System"));
2471 }
2472 
requestRoutesFaultLogDumpService(App & app)2473 inline void requestRoutesFaultLogDumpService(App& app)
2474 {
2475     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/")
2476         .privileges(redfish::privileges::getLogService)
2477         .methods(boost::beast::http::verb::get)(std::bind_front(
2478             handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
2479 }
2480 
requestRoutesFaultLogDumpEntryCollection(App & app)2481 inline void requestRoutesFaultLogDumpEntryCollection(App& app)
2482 {
2483     BMCWEB_ROUTE(app,
2484                  "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/")
2485         .privileges(redfish::privileges::getLogEntryCollection)
2486         .methods(boost::beast::http::verb::get)(
2487             std::bind_front(handleLogServicesDumpEntriesCollectionGet,
2488                             std::ref(app), "FaultLog"));
2489 }
2490 
requestRoutesFaultLogDumpEntry(App & app)2491 inline void requestRoutesFaultLogDumpEntry(App& app)
2492 {
2493     BMCWEB_ROUTE(
2494         app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
2495         .privileges(redfish::privileges::getLogEntry)
2496         .methods(boost::beast::http::verb::get)(std::bind_front(
2497             handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
2498 
2499     BMCWEB_ROUTE(
2500         app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
2501         .privileges(redfish::privileges::deleteLogEntry)
2502         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2503             handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
2504 }
2505 
requestRoutesFaultLogDumpClear(App & app)2506 inline void requestRoutesFaultLogDumpClear(App& app)
2507 {
2508     BMCWEB_ROUTE(
2509         app,
2510         "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/")
2511         .privileges(redfish::privileges::postLogService)
2512         .methods(boost::beast::http::verb::post)(std::bind_front(
2513             handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
2514 }
2515 
requestRoutesSystemDumpService(App & app)2516 inline void requestRoutesSystemDumpService(App& app)
2517 {
2518     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/")
2519         .privileges(redfish::privileges::getLogService)
2520         .methods(boost::beast::http::verb::get)(std::bind_front(
2521             handleLogServicesDumpServiceComputerSystemGet, std::ref(app)));
2522 }
2523 
requestRoutesSystemDumpEntryCollection(App & app)2524 inline void requestRoutesSystemDumpEntryCollection(App& app)
2525 {
2526     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/")
2527         .privileges(redfish::privileges::getLogEntryCollection)
2528         .methods(boost::beast::http::verb::get)(std::bind_front(
2529             handleLogServicesDumpEntriesCollectionComputerSystemGet,
2530             std::ref(app)));
2531 }
2532 
requestRoutesSystemDumpEntry(App & app)2533 inline void requestRoutesSystemDumpEntry(App& app)
2534 {
2535     BMCWEB_ROUTE(app,
2536                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2537         .privileges(redfish::privileges::getLogEntry)
2538         .methods(boost::beast::http::verb::get)(std::bind_front(
2539             handleLogServicesDumpEntryComputerSystemGet, std::ref(app)));
2540 
2541     BMCWEB_ROUTE(app,
2542                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
2543         .privileges(redfish::privileges::deleteLogEntry)
2544         .methods(boost::beast::http::verb::delete_)(std::bind_front(
2545             handleLogServicesDumpEntryComputerSystemDelete, std::ref(app)));
2546 }
2547 
requestRoutesSystemDumpCreate(App & app)2548 inline void requestRoutesSystemDumpCreate(App& app)
2549 {
2550     BMCWEB_ROUTE(
2551         app,
2552         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
2553         .privileges(redfish::privileges::postLogService)
2554         .methods(boost::beast::http::verb::post)(std::bind_front(
2555             handleLogServicesDumpCollectDiagnosticDataComputerSystemPost,
2556             std::ref(app)));
2557 }
2558 
requestRoutesSystemDumpClear(App & app)2559 inline void requestRoutesSystemDumpClear(App& app)
2560 {
2561     BMCWEB_ROUTE(
2562         app,
2563         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
2564         .privileges(redfish::privileges::postLogService)
2565         .methods(boost::beast::http::verb::post)(std::bind_front(
2566             handleLogServicesDumpClearLogComputerSystemPost, std::ref(app)));
2567 }
2568 
requestRoutesCrashdumpService(App & app)2569 inline void requestRoutesCrashdumpService(App& app)
2570 {
2571     // Note: Deviated from redfish privilege registry for GET & HEAD
2572     // method for security reasons.
2573     /**
2574      * Functions triggers appropriate requests on DBus
2575      */
2576     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/")
2577         // This is incorrect, should be:
2578         //.privileges(redfish::privileges::getLogService)
2579         .privileges({{"ConfigureManager"}})
2580         .methods(
2581             boost::beast::http::verb::
2582                 get)([&app](const crow::Request& req,
2583                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2584                             const std::string& systemName) {
2585             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2586             {
2587                 return;
2588             }
2589             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2590             {
2591                 // Option currently returns no systems.  TBD
2592                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2593                                            systemName);
2594                 return;
2595             }
2596             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2597             {
2598                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2599                                            systemName);
2600                 return;
2601             }
2602 
2603             // Copy over the static data to include the entries added by
2604             // SubRoute
2605             asyncResp->res.jsonValue["@odata.id"] =
2606                 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
2607                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
2608             asyncResp->res.jsonValue["@odata.type"] =
2609                 "#LogService.v1_2_0.LogService";
2610             asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
2611             asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
2612             asyncResp->res.jsonValue["Id"] = "Crashdump";
2613             asyncResp->res.jsonValue["OverWritePolicy"] =
2614                 log_service::OverWritePolicy::WrapsWhenFull;
2615             asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
2616 
2617             std::pair<std::string, std::string> redfishDateTimeOffset =
2618                 redfish::time_utils::getDateTimeOffsetNow();
2619             asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
2620             asyncResp->res.jsonValue["DateTimeLocalOffset"] =
2621                 redfishDateTimeOffset.second;
2622 
2623             asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
2624                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
2625                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2626             asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
2627                                     ["target"] = std::format(
2628                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog",
2629                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2630             asyncResp->res
2631                 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
2632                           ["target"] = std::format(
2633                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData",
2634                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
2635         });
2636 }
2637 
requestRoutesCrashdumpClear(App & app)2638 void inline requestRoutesCrashdumpClear(App& app)
2639 {
2640     BMCWEB_ROUTE(
2641         app,
2642         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/")
2643         // This is incorrect, should be:
2644         //.privileges(redfish::privileges::postLogService)
2645         .privileges({{"ConfigureComponents"}})
2646         .methods(boost::beast::http::verb::post)(
2647             [&app](const crow::Request& req,
2648                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2649                    const std::string& systemName) {
2650                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2651                 {
2652                     return;
2653                 }
2654                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2655                 {
2656                     // Option currently returns no systems.  TBD
2657                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2658                                                systemName);
2659                     return;
2660                 }
2661                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2662                 {
2663                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2664                                                systemName);
2665                     return;
2666                 }
2667                 dbus::utility::async_method_call(
2668                     asyncResp,
2669                     [asyncResp](const boost::system::error_code& ec,
2670                                 const std::string&) {
2671                         if (ec)
2672                         {
2673                             messages::internalError(asyncResp->res);
2674                             return;
2675                         }
2676                         messages::success(asyncResp->res);
2677                     },
2678                     crashdumpObject, crashdumpPath, deleteAllInterface,
2679                     "DeleteAll");
2680             });
2681 }
2682 
logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & logID,nlohmann::json & logEntryJson)2683 inline void logCrashdumpEntry(
2684     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2685     const std::string& logID, nlohmann::json& logEntryJson)
2686 {
2687     auto getStoredLogCallback =
2688         [asyncResp, logID,
2689          &logEntryJson](const boost::system::error_code& ec,
2690                         const dbus::utility::DBusPropertiesMap& params) {
2691             if (ec)
2692             {
2693                 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message());
2694                 if (ec.value() ==
2695                     boost::system::linux_error::bad_request_descriptor)
2696                 {
2697                     messages::resourceNotFound(asyncResp->res, "LogEntry",
2698                                                logID);
2699                 }
2700                 else
2701                 {
2702                     messages::internalError(asyncResp->res);
2703                 }
2704                 return;
2705             }
2706 
2707             std::string timestamp{};
2708             std::string filename{};
2709             std::string logfile{};
2710             parseCrashdumpParameters(params, filename, timestamp, logfile);
2711 
2712             if (filename.empty() || timestamp.empty())
2713             {
2714                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
2715                 return;
2716             }
2717 
2718             std::string crashdumpURI =
2719                 std::format(
2720                     "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/",
2721                     BMCWEB_REDFISH_SYSTEM_URI_NAME) +
2722                 logID + "/" + filename;
2723             nlohmann::json::object_t logEntry;
2724             logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
2725             logEntry["@odata.id"] = boost::urls::format(
2726                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}",
2727                 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID);
2728             logEntry["Name"] = "CPU Crashdump";
2729             logEntry["Id"] = logID;
2730             logEntry["EntryType"] = log_entry::LogEntryType::Oem;
2731             logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
2732             logEntry["DiagnosticDataType"] = "OEM";
2733             logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
2734             logEntry["Created"] = std::move(timestamp);
2735 
2736             // If logEntryJson references an array of LogEntry resources
2737             // ('Members' list), then push this as a new entry, otherwise set it
2738             // directly
2739             if (logEntryJson.is_array())
2740             {
2741                 logEntryJson.push_back(logEntry);
2742                 asyncResp->res.jsonValue["Members@odata.count"] =
2743                     logEntryJson.size();
2744             }
2745             else
2746             {
2747                 logEntryJson.update(logEntry);
2748             }
2749         };
2750     dbus::utility::getAllProperties(
2751         crashdumpObject, crashdumpPath + std::string("/") + logID,
2752         crashdumpInterface, std::move(getStoredLogCallback));
2753 }
2754 
requestRoutesCrashdumpEntryCollection(App & app)2755 inline void requestRoutesCrashdumpEntryCollection(App& app)
2756 {
2757     // Note: Deviated from redfish privilege registry for GET & HEAD
2758     // method for security reasons.
2759     /**
2760      * Functions triggers appropriate requests on DBus
2761      */
2762     BMCWEB_ROUTE(app,
2763                  "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/")
2764         // This is incorrect, should be.
2765         //.privileges(redfish::privileges::postLogEntryCollection)
2766         .privileges({{"ConfigureComponents"}})
2767         .methods(
2768             boost::beast::http::verb::
2769                 get)([&app](const crow::Request& req,
2770                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2771                             const std::string& systemName) {
2772             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2773             {
2774                 return;
2775             }
2776             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2777             {
2778                 // Option currently returns no systems.  TBD
2779                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2780                                            systemName);
2781                 return;
2782             }
2783             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2784             {
2785                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2786                                            systemName);
2787                 return;
2788             }
2789 
2790             constexpr std::array<std::string_view, 1> interfaces = {
2791                 crashdumpInterface};
2792             dbus::utility::getSubTreePaths(
2793                 "/", 0, interfaces,
2794                 [asyncResp](const boost::system::error_code& ec,
2795                             const std::vector<std::string>& resp) {
2796                     if (ec)
2797                     {
2798                         if (ec.value() !=
2799                             boost::system::errc::no_such_file_or_directory)
2800                         {
2801                             BMCWEB_LOG_DEBUG("failed to get entries ec: {}",
2802                                              ec.message());
2803                             messages::internalError(asyncResp->res);
2804                             return;
2805                         }
2806                     }
2807                     asyncResp->res.jsonValue["@odata.type"] =
2808                         "#LogEntryCollection.LogEntryCollection";
2809                     asyncResp->res.jsonValue["@odata.id"] = std::format(
2810                         "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
2811                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
2812                     asyncResp->res.jsonValue["Name"] =
2813                         "Open BMC Crashdump Entries";
2814                     asyncResp->res.jsonValue["Description"] =
2815                         "Collection of Crashdump Entries";
2816                     asyncResp->res.jsonValue["Members"] =
2817                         nlohmann::json::array();
2818                     asyncResp->res.jsonValue["Members@odata.count"] = 0;
2819 
2820                     for (const std::string& path : resp)
2821                     {
2822                         const sdbusplus::message::object_path objPath(path);
2823                         // Get the log ID
2824                         std::string logID = objPath.filename();
2825                         if (logID.empty())
2826                         {
2827                             continue;
2828                         }
2829                         // Add the log entry to the array
2830                         logCrashdumpEntry(asyncResp, logID,
2831                                           asyncResp->res.jsonValue["Members"]);
2832                     }
2833                 });
2834         });
2835 }
2836 
requestRoutesCrashdumpEntry(App & app)2837 inline void requestRoutesCrashdumpEntry(App& app)
2838 {
2839     // Note: Deviated from redfish privilege registry for GET & HEAD
2840     // method for security reasons.
2841 
2842     BMCWEB_ROUTE(
2843         app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/")
2844         // this is incorrect, should be
2845         // .privileges(redfish::privileges::getLogEntry)
2846         .privileges({{"ConfigureComponents"}})
2847         .methods(boost::beast::http::verb::get)(
2848             [&app](const crow::Request& req,
2849                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2850                    const std::string& systemName, const std::string& param) {
2851                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2852                 {
2853                     return;
2854                 }
2855                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2856                 {
2857                     // Option currently returns no systems.  TBD
2858                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2859                                                systemName);
2860                     return;
2861                 }
2862                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2863                 {
2864                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2865                                                systemName);
2866                     return;
2867                 }
2868                 const std::string& logID = param;
2869                 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
2870             });
2871 }
2872 
requestRoutesCrashdumpFile(App & app)2873 inline void requestRoutesCrashdumpFile(App& app)
2874 {
2875     // Note: Deviated from redfish privilege registry for GET & HEAD
2876     // method for security reasons.
2877     BMCWEB_ROUTE(
2878         app,
2879         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/")
2880         .privileges(redfish::privileges::getLogEntry)
2881         .methods(boost::beast::http::verb::get)(
2882             [](const crow::Request& req,
2883                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2884                const std::string& systemName, const std::string& logID,
2885                const std::string& fileName) {
2886                 // Do not call getRedfishRoute here since the crashdump file is
2887                 // not a Redfish resource.
2888 
2889                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2890                 {
2891                     // Option currently returns no systems.  TBD
2892                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2893                                                systemName);
2894                     return;
2895                 }
2896                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2897                 {
2898                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2899                                                systemName);
2900                     return;
2901                 }
2902 
2903                 auto getStoredLogCallback =
2904                     [asyncResp, logID, fileName,
2905                      url(boost::urls::url(req.url()))](
2906                         const boost::system::error_code& ec,
2907                         const std::vector<std::pair<
2908                             std::string, dbus::utility::DbusVariantType>>&
2909                             resp) {
2910                         if (ec)
2911                         {
2912                             BMCWEB_LOG_DEBUG("failed to get log ec: {}",
2913                                              ec.message());
2914                             messages::internalError(asyncResp->res);
2915                             return;
2916                         }
2917 
2918                         std::string dbusFilename{};
2919                         std::string dbusTimestamp{};
2920                         std::string dbusFilepath{};
2921 
2922                         parseCrashdumpParameters(resp, dbusFilename,
2923                                                  dbusTimestamp, dbusFilepath);
2924 
2925                         if (dbusFilename.empty() || dbusTimestamp.empty() ||
2926                             dbusFilepath.empty())
2927                         {
2928                             messages::resourceNotFound(asyncResp->res,
2929                                                        "LogEntry", logID);
2930                             return;
2931                         }
2932 
2933                         // Verify the file name parameter is correct
2934                         if (fileName != dbusFilename)
2935                         {
2936                             messages::resourceNotFound(asyncResp->res,
2937                                                        "LogEntry", logID);
2938                             return;
2939                         }
2940 
2941                         if (asyncResp->res.openFile(dbusFilepath) !=
2942                             crow::OpenCode::Success)
2943                         {
2944                             messages::resourceNotFound(asyncResp->res,
2945                                                        "LogEntry", logID);
2946                             return;
2947                         }
2948 
2949                         // Configure this to be a file download when accessed
2950                         // from a browser
2951                         asyncResp->res.addHeader(
2952                             boost::beast::http::field::content_disposition,
2953                             "attachment");
2954                     };
2955                 dbus::utility::getAllProperties(
2956                     *crow::connections::systemBus, crashdumpObject,
2957                     crashdumpPath + std::string("/") + logID,
2958                     crashdumpInterface, std::move(getStoredLogCallback));
2959             });
2960 }
2961 
2962 enum class OEMDiagnosticType
2963 {
2964     onDemand,
2965     telemetry,
2966     invalid,
2967 };
2968 
getOEMDiagnosticType(std::string_view oemDiagStr)2969 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr)
2970 {
2971     if (oemDiagStr == "OnDemand")
2972     {
2973         return OEMDiagnosticType::onDemand;
2974     }
2975     if (oemDiagStr == "Telemetry")
2976     {
2977         return OEMDiagnosticType::telemetry;
2978     }
2979 
2980     return OEMDiagnosticType::invalid;
2981 }
2982 
requestRoutesCrashdumpCollect(App & app)2983 inline void requestRoutesCrashdumpCollect(App& app)
2984 {
2985     // Note: Deviated from redfish privilege registry for GET & HEAD
2986     // method for security reasons.
2987     BMCWEB_ROUTE(
2988         app,
2989         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
2990         // The below is incorrect;  Should be ConfigureManager
2991         //.privileges(redfish::privileges::postLogService)
2992         .privileges({{"ConfigureComponents"}})
2993         .methods(boost::beast::http::verb::post)(
2994             [&app](const crow::Request& req,
2995                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2996                    const std::string& systemName) {
2997                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2998                 {
2999                     return;
3000                 }
3001 
3002                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
3003                 {
3004                     // Option currently returns no systems.  TBD
3005                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3006                                                systemName);
3007                     return;
3008                 }
3009                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
3010                 {
3011                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3012                                                systemName);
3013                     return;
3014                 }
3015 
3016                 std::string diagnosticDataType;
3017                 std::string oemDiagnosticDataType;
3018                 if (!redfish::json_util::readJsonAction(               //
3019                         req, asyncResp->res,                           //
3020                         "DiagnosticDataType", diagnosticDataType,      //
3021                         "OEMDiagnosticDataType", oemDiagnosticDataType //
3022                         ))
3023                 {
3024                     return;
3025                 }
3026 
3027                 if (diagnosticDataType != "OEM")
3028                 {
3029                     BMCWEB_LOG_ERROR(
3030                         "Only OEM DiagnosticDataType supported for Crashdump");
3031                     messages::actionParameterValueFormatError(
3032                         asyncResp->res, diagnosticDataType,
3033                         "DiagnosticDataType", "CollectDiagnosticData");
3034                     return;
3035                 }
3036 
3037                 OEMDiagnosticType oemDiagType =
3038                     getOEMDiagnosticType(oemDiagnosticDataType);
3039 
3040                 std::string iface;
3041                 std::string method;
3042                 std::string taskMatchStr;
3043                 if (oemDiagType == OEMDiagnosticType::onDemand)
3044                 {
3045                     iface = crashdumpOnDemandInterface;
3046                     method = "GenerateOnDemandLog";
3047                     taskMatchStr =
3048                         "type='signal',"
3049                         "interface='org.freedesktop.DBus.Properties',"
3050                         "member='PropertiesChanged',"
3051                         "arg0namespace='com.intel.crashdump'";
3052                 }
3053                 else if (oemDiagType == OEMDiagnosticType::telemetry)
3054                 {
3055                     iface = crashdumpTelemetryInterface;
3056                     method = "GenerateTelemetryLog";
3057                     taskMatchStr =
3058                         "type='signal',"
3059                         "interface='org.freedesktop.DBus.Properties',"
3060                         "member='PropertiesChanged',"
3061                         "arg0namespace='com.intel.crashdump'";
3062                 }
3063                 else
3064                 {
3065                     BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}",
3066                                      oemDiagnosticDataType);
3067                     messages::actionParameterValueFormatError(
3068                         asyncResp->res, oemDiagnosticDataType,
3069                         "OEMDiagnosticDataType", "CollectDiagnosticData");
3070                     return;
3071                 }
3072 
3073                 auto collectCrashdumpCallback =
3074                     [asyncResp, payload(task::Payload(req)),
3075                      taskMatchStr](const boost::system::error_code& ec,
3076                                    const std::string&) mutable {
3077                         if (ec)
3078                         {
3079                             if (ec.value() ==
3080                                 boost::system::errc::operation_not_supported)
3081                             {
3082                                 messages::resourceInStandby(asyncResp->res);
3083                             }
3084                             else if (ec.value() == boost::system::errc::
3085                                                        device_or_resource_busy)
3086                             {
3087                                 messages::serviceTemporarilyUnavailable(
3088                                     asyncResp->res, "60");
3089                             }
3090                             else
3091                             {
3092                                 messages::internalError(asyncResp->res);
3093                             }
3094                             return;
3095                         }
3096                         std::shared_ptr<task::TaskData> task =
3097                             task::TaskData::createTask(
3098                                 [](const boost::system::error_code& ec2,
3099                                    sdbusplus::message_t&,
3100                                    const std::shared_ptr<task::TaskData>&
3101                                        taskData) {
3102                                     if (!ec2)
3103                                     {
3104                                         taskData->messages.emplace_back(
3105                                             messages::taskCompletedOK(
3106                                                 std::to_string(
3107                                                     taskData->index)));
3108                                         taskData->state = "Completed";
3109                                     }
3110                                     return task::completed;
3111                                 },
3112                                 taskMatchStr);
3113 
3114                         task->startTimer(std::chrono::minutes(5));
3115                         task->populateResp(asyncResp->res);
3116                         task->payload.emplace(std::move(payload));
3117                     };
3118 
3119                 dbus::utility::async_method_call(
3120                     asyncResp, std::move(collectCrashdumpCallback),
3121                     crashdumpObject, crashdumpPath, iface, method);
3122             });
3123 }
3124 
dBusLogServiceActionsClear(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp)3125 inline void dBusLogServiceActionsClear(
3126     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
3127 {
3128     BMCWEB_LOG_DEBUG("Do delete all entries.");
3129 
3130     // Process response from Logging service.
3131     auto respHandler = [asyncResp](const boost::system::error_code& ec) {
3132         BMCWEB_LOG_DEBUG("doClearLog resp_handler callback: Done");
3133         if (ec)
3134         {
3135             // TODO Handle for specific error code
3136             BMCWEB_LOG_ERROR("doClearLog resp_handler got error {}", ec);
3137             asyncResp->res.result(
3138                 boost::beast::http::status::internal_server_error);
3139             return;
3140         }
3141 
3142         asyncResp->res.result(boost::beast::http::status::no_content);
3143     };
3144 
3145     // Make call to Logging service to request Clear Log
3146     dbus::utility::async_method_call(
3147         asyncResp, respHandler, "xyz.openbmc_project.Logging",
3148         "/xyz/openbmc_project/logging",
3149         "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
3150 }
3151 
3152 /**
3153  * DBusLogServiceActionsClear class supports POST method for ClearLog action.
3154  */
requestRoutesDBusLogServiceActionsClear(App & app)3155 inline void requestRoutesDBusLogServiceActionsClear(App& app)
3156 {
3157     /**
3158      * Function handles POST method request.
3159      * The Clear Log actions does not require any parameter.The action deletes
3160      * all entries found in the Entries collection for this Log Service.
3161      */
3162 
3163     BMCWEB_ROUTE(
3164         app,
3165         "/redfish/v1/Systems/<str>/LogServices/EventLog/Actions/LogService.ClearLog/")
3166         .privileges(redfish::privileges::postLogService)
3167         .methods(boost::beast::http::verb::post)(
3168             [&app](const crow::Request& req,
3169                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
3170                    const std::string& systemName) {
3171                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
3172                 {
3173                     return;
3174                 }
3175                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
3176                 {
3177                     // Option currently returns no systems.  TBD
3178                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3179                                                systemName);
3180                     return;
3181                 }
3182                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
3183                 {
3184                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
3185                                                systemName);
3186                     return;
3187                 }
3188                 dBusLogServiceActionsClear(asyncResp);
3189             });
3190 }
3191 
3192 } // namespace redfish
3193