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