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