xref: /openbmc/bmcweb/redfish-core/lib/log_services.hpp (revision 43feb5cd47bd6d87a927a1ec63024ac0eb573bac)
1 // SPDX-License-Identifier: Apache-2.0
2 // SPDX-FileCopyrightText: Copyright OpenBMC Authors
3 // SPDX-FileCopyrightText: Copyright 2018 Intel Corporation
4 #pragma once
5 
6 #include "bmcweb_config.h"
7 
8 #include "app.hpp"
9 #include "async_resp.hpp"
10 #include "dbus_singleton.hpp"
11 #include "dbus_utility.hpp"
12 #include "error_messages.hpp"
13 #include "generated/enums/log_entry.hpp"
14 #include "generated/enums/log_service.hpp"
15 #include "http_body.hpp"
16 #include "http_request.hpp"
17 #include "http_response.hpp"
18 #include "human_sort.hpp"
19 #include "logging.hpp"
20 #include "query.hpp"
21 #include "registries/privilege_registry.hpp"
22 #include "str_utility.hpp"
23 #include "task.hpp"
24 #include "task_messages.hpp"
25 #include "utils/dbus_utils.hpp"
26 #include "utils/etag_utils.hpp"
27 #include "utils/eventlog_utils.hpp"
28 #include "utils/json_utils.hpp"
29 #include "utils/log_services_utils.hpp"
30 #include "utils/time_utils.hpp"
31 
32 #include <asm-generic/errno.h>
33 #include <systemd/sd-bus.h>
34 #include <tinyxml2.h>
35 #include <unistd.h>
36 
37 #include <boost/beast/http/field.hpp>
38 #include <boost/beast/http/status.hpp>
39 #include <boost/beast/http/verb.hpp>
40 #include <boost/system/linux_error.hpp>
41 #include <boost/url/format.hpp>
42 #include <boost/url/url.hpp>
43 #include <sdbusplus/message.hpp>
44 #include <sdbusplus/message/native_types.hpp>
45 #include <sdbusplus/unpack_properties.hpp>
46 
47 #include <algorithm>
48 #include <array>
49 #include <chrono>
50 #include <cstdint>
51 #include <filesystem>
52 #include <format>
53 #include <functional>
54 #include <iterator>
55 #include <memory>
56 #include <optional>
57 #include <ranges>
58 #include <span>
59 #include <string>
60 #include <string_view>
61 #include <utility>
62 #include <variant>
63 #include <vector>
64 
65 namespace redfish
66 {
67 
68 constexpr const char* crashdumpObject = "com.intel.crashdump";
69 constexpr const char* crashdumpPath = "/com/intel/crashdump";
70 constexpr const char* crashdumpInterface = "com.intel.crashdump";
71 constexpr const char* deleteAllInterface =
72     "xyz.openbmc_project.Collection.DeleteAll";
73 constexpr const char* crashdumpOnDemandInterface =
74     "com.intel.crashdump.OnDemand";
75 constexpr const char* crashdumpTelemetryInterface =
76     "com.intel.crashdump.Telemetry";
77 
78 enum class DumpCreationProgress
79 {
80     DUMP_CREATE_SUCCESS,
81     DUMP_CREATE_FAILED,
82     DUMP_CREATE_INPROGRESS
83 };
84 
getDumpPath(std::string_view dumpType)85 inline std::string getDumpPath(std::string_view dumpType)
86 {
87     std::string dbusDumpPath = "/xyz/openbmc_project/dump/";
88     std::ranges::transform(dumpType, std::back_inserter(dbusDumpPath),
89                            bmcweb::asciiToLower);
90 
91     return dbusDumpPath;
92 }
93 
mapDbusOriginatorTypeToRedfish(const std::string & originatorType)94 inline log_entry::OriginatorTypes mapDbusOriginatorTypeToRedfish(
95     const std::string& originatorType)
96 {
97     if (originatorType ==
98         "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client")
99     {
100         return log_entry::OriginatorTypes::Client;
101     }
102     if (originatorType ==
103         "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Internal")
104     {
105         return log_entry::OriginatorTypes::Internal;
106     }
107     if (originatorType ==
108         "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.SupportingService")
109     {
110         return log_entry::OriginatorTypes::SupportingService;
111     }
112     return log_entry::OriginatorTypes::Invalid;
113 }
114 
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)115 inline void parseDumpEntryFromDbusObject(
116     const dbus::utility::ManagedObjectType::value_type& object,
117     std::string& dumpStatus, uint64_t& size, uint64_t& timestampUs,
118     std::string& originatorId, log_entry::OriginatorTypes& originatorType,
119     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp)
120 {
121     for (const auto& interfaceMap : object.second)
122     {
123         if (interfaceMap.first == "xyz.openbmc_project.Common.Progress")
124         {
125             for (const auto& propertyMap : interfaceMap.second)
126             {
127                 if (propertyMap.first == "Status")
128                 {
129                     const auto* status =
130                         std::get_if<std::string>(&propertyMap.second);
131                     if (status == nullptr)
132                     {
133                         messages::internalError(asyncResp->res);
134                         break;
135                     }
136                     dumpStatus = *status;
137                 }
138             }
139         }
140         else if (interfaceMap.first == "xyz.openbmc_project.Dump.Entry")
141         {
142             for (const auto& propertyMap : interfaceMap.second)
143             {
144                 if (propertyMap.first == "Size")
145                 {
146                     const auto* sizePtr =
147                         std::get_if<uint64_t>(&propertyMap.second);
148                     if (sizePtr == nullptr)
149                     {
150                         messages::internalError(asyncResp->res);
151                         break;
152                     }
153                     size = *sizePtr;
154                     break;
155                 }
156             }
157         }
158         else if (interfaceMap.first == "xyz.openbmc_project.Time.EpochTime")
159         {
160             for (const auto& propertyMap : interfaceMap.second)
161             {
162                 if (propertyMap.first == "Elapsed")
163                 {
164                     const uint64_t* usecsTimeStamp =
165                         std::get_if<uint64_t>(&propertyMap.second);
166                     if (usecsTimeStamp == nullptr)
167                     {
168                         messages::internalError(asyncResp->res);
169                         break;
170                     }
171                     timestampUs = *usecsTimeStamp;
172                     break;
173                 }
174             }
175         }
176         else if (interfaceMap.first ==
177                  "xyz.openbmc_project.Common.OriginatedBy")
178         {
179             for (const auto& propertyMap : interfaceMap.second)
180             {
181                 if (propertyMap.first == "OriginatorId")
182                 {
183                     const std::string* id =
184                         std::get_if<std::string>(&propertyMap.second);
185                     if (id == nullptr)
186                     {
187                         messages::internalError(asyncResp->res);
188                         break;
189                     }
190                     originatorId = *id;
191                 }
192 
193                 if (propertyMap.first == "OriginatorType")
194                 {
195                     const std::string* type =
196                         std::get_if<std::string>(&propertyMap.second);
197                     if (type == nullptr)
198                     {
199                         messages::internalError(asyncResp->res);
200                         break;
201                     }
202 
203                     originatorType = mapDbusOriginatorTypeToRedfish(*type);
204                     if (originatorType == log_entry::OriginatorTypes::Invalid)
205                     {
206                         messages::internalError(asyncResp->res);
207                         break;
208                     }
209                 }
210             }
211         }
212     }
213 }
214 
getDumpEntriesPath(const std::string & dumpType)215 static std::string getDumpEntriesPath(const std::string& dumpType)
216 {
217     std::string entriesPath;
218 
219     if (dumpType == "BMC")
220     {
221         entriesPath =
222             std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/",
223                         BMCWEB_REDFISH_MANAGER_URI_NAME);
224     }
225     else if (dumpType == "FaultLog")
226     {
227         entriesPath =
228             std::format("/redfish/v1/Managers/{}/LogServices/FaultLog/Entries/",
229                         BMCWEB_REDFISH_MANAGER_URI_NAME);
230     }
231     else if (dumpType == "System")
232     {
233         entriesPath =
234             std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/",
235                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
236     }
237     else
238     {
239         BMCWEB_LOG_ERROR("getDumpEntriesPath() invalid dump type: {}",
240                          dumpType);
241     }
242 
243     // Returns empty string on error
244     return entriesPath;
245 }
246 
getDumpEntryCollection(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)247 inline void getDumpEntryCollection(
248     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
249     const std::string& dumpType)
250 {
251     std::string entriesPath = getDumpEntriesPath(dumpType);
252     if (entriesPath.empty())
253     {
254         messages::internalError(asyncResp->res);
255         return;
256     }
257 
258     sdbusplus::message::object_path path("/xyz/openbmc_project/dump");
259     dbus::utility::getManagedObjects(
260         "xyz.openbmc_project.Dump.Manager", path,
261         [asyncResp, entriesPath,
262          dumpType](const boost::system::error_code& ec,
263                    const dbus::utility::ManagedObjectType& objects) {
264             if (ec)
265             {
266                 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec);
267                 messages::internalError(asyncResp->res);
268                 return;
269             }
270 
271             // Remove ending slash
272             std::string odataIdStr = entriesPath;
273             if (!odataIdStr.empty())
274             {
275                 odataIdStr.pop_back();
276             }
277 
278             asyncResp->res.jsonValue["@odata.type"] =
279                 "#LogEntryCollection.LogEntryCollection";
280             asyncResp->res.jsonValue["@odata.id"] = std::move(odataIdStr);
281             asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entries";
282             asyncResp->res.jsonValue["Description"] =
283                 "Collection of " + dumpType + " Dump Entries";
284 
285             nlohmann::json::array_t entriesArray;
286             std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/";
287 
288             dbus::utility::ManagedObjectType resp(objects);
289             std::ranges::sort(resp, [](const auto& l, const auto& r) {
290                 return AlphanumLess<std::string>()(l.first.filename(),
291                                                    r.first.filename());
292             });
293 
294             for (auto& object : resp)
295             {
296                 if (object.first.str.find(dumpEntryPath) == std::string::npos)
297                 {
298                     continue;
299                 }
300                 uint64_t timestampUs = 0;
301                 uint64_t size = 0;
302                 std::string dumpStatus;
303                 std::string originatorId;
304                 log_entry::OriginatorTypes originatorType =
305                     log_entry::OriginatorTypes::Internal;
306                 nlohmann::json::object_t thisEntry;
307 
308                 std::string entryID = object.first.filename();
309                 if (entryID.empty())
310                 {
311                     continue;
312                 }
313 
314                 parseDumpEntryFromDbusObject(object, dumpStatus, size,
315                                              timestampUs, originatorId,
316                                              originatorType, asyncResp);
317 
318                 if (dumpStatus !=
319                         "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
320                     !dumpStatus.empty())
321                 {
322                     // Dump status is not Complete, no need to enumerate
323                     continue;
324                 }
325 
326                 thisEntry["@odata.type"] = "#LogEntry.v1_11_0.LogEntry";
327                 thisEntry["@odata.id"] = entriesPath + entryID;
328                 thisEntry["Id"] = entryID;
329                 thisEntry["EntryType"] = "Event";
330                 thisEntry["Name"] = dumpType + " Dump Entry";
331                 thisEntry["Created"] =
332                     redfish::time_utils::getDateTimeUintUs(timestampUs);
333 
334                 if (!originatorId.empty())
335                 {
336                     thisEntry["Originator"] = originatorId;
337                     thisEntry["OriginatorType"] = originatorType;
338                 }
339 
340                 if (dumpType == "BMC")
341                 {
342                     thisEntry["DiagnosticDataType"] = "Manager";
343                     thisEntry["AdditionalDataURI"] =
344                         entriesPath + entryID + "/attachment";
345                     thisEntry["AdditionalDataSizeBytes"] = size;
346                 }
347                 else if (dumpType == "System")
348                 {
349                     thisEntry["DiagnosticDataType"] = "OEM";
350                     thisEntry["OEMDiagnosticDataType"] = "System";
351                     thisEntry["AdditionalDataURI"] =
352                         entriesPath + entryID + "/attachment";
353                     thisEntry["AdditionalDataSizeBytes"] = size;
354                 }
355                 entriesArray.emplace_back(std::move(thisEntry));
356             }
357             asyncResp->res.jsonValue["Members@odata.count"] =
358                 entriesArray.size();
359             asyncResp->res.jsonValue["Members"] = std::move(entriesArray);
360         });
361 }
362 
getDumpEntryById(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & dumpType)363 inline void getDumpEntryById(
364     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
365     const std::string& entryID, const std::string& dumpType)
366 {
367     std::string entriesPath = getDumpEntriesPath(dumpType);
368     if (entriesPath.empty())
369     {
370         messages::internalError(asyncResp->res);
371         return;
372     }
373 
374     sdbusplus::message::object_path path("/xyz/openbmc_project/dump");
375     dbus::utility::getManagedObjects(
376         "xyz.openbmc_project.Dump.Manager", path,
377         [asyncResp, entryID, dumpType,
378          entriesPath](const boost::system::error_code& ec,
379                       const dbus::utility::ManagedObjectType& resp) {
380             if (ec)
381             {
382                 BMCWEB_LOG_ERROR("DumpEntry resp_handler got error {}", ec);
383                 messages::internalError(asyncResp->res);
384                 return;
385             }
386 
387             bool foundDumpEntry = false;
388             std::string dumpEntryPath = getDumpPath(dumpType) + "/entry/";
389 
390             for (const auto& objectPath : resp)
391             {
392                 if (objectPath.first.str != dumpEntryPath + entryID)
393                 {
394                     continue;
395                 }
396 
397                 foundDumpEntry = true;
398                 uint64_t timestampUs = 0;
399                 uint64_t size = 0;
400                 std::string dumpStatus;
401                 std::string originatorId;
402                 log_entry::OriginatorTypes originatorType =
403                     log_entry::OriginatorTypes::Internal;
404 
405                 parseDumpEntryFromDbusObject(objectPath, dumpStatus, size,
406                                              timestampUs, originatorId,
407                                              originatorType, asyncResp);
408 
409                 if (dumpStatus !=
410                         "xyz.openbmc_project.Common.Progress.OperationStatus.Completed" &&
411                     !dumpStatus.empty())
412                 {
413                     // Dump status is not Complete
414                     // return not found until status is changed to Completed
415                     messages::resourceNotFound(asyncResp->res,
416                                                dumpType + " dump", entryID);
417                     return;
418                 }
419 
420                 asyncResp->res.jsonValue["@odata.type"] =
421                     "#LogEntry.v1_11_0.LogEntry";
422                 asyncResp->res.jsonValue["@odata.id"] = entriesPath + entryID;
423                 asyncResp->res.jsonValue["Id"] = entryID;
424                 asyncResp->res.jsonValue["EntryType"] = "Event";
425                 asyncResp->res.jsonValue["Name"] = dumpType + " Dump Entry";
426                 asyncResp->res.jsonValue["Created"] =
427                     redfish::time_utils::getDateTimeUintUs(timestampUs);
428 
429                 if (!originatorId.empty())
430                 {
431                     asyncResp->res.jsonValue["Originator"] = originatorId;
432                     asyncResp->res.jsonValue["OriginatorType"] = originatorType;
433                 }
434 
435                 if (dumpType == "BMC")
436                 {
437                     asyncResp->res.jsonValue["DiagnosticDataType"] = "Manager";
438                     asyncResp->res.jsonValue["AdditionalDataURI"] =
439                         entriesPath + entryID + "/attachment";
440                     asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
441                 }
442                 else if (dumpType == "System")
443                 {
444                     asyncResp->res.jsonValue["DiagnosticDataType"] = "OEM";
445                     asyncResp->res.jsonValue["OEMDiagnosticDataType"] =
446                         "System";
447                     asyncResp->res.jsonValue["AdditionalDataURI"] =
448                         entriesPath + entryID + "/attachment";
449                     asyncResp->res.jsonValue["AdditionalDataSizeBytes"] = size;
450                 }
451             }
452             if (!foundDumpEntry)
453             {
454                 BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID);
455                 messages::resourceNotFound(asyncResp->res, dumpType + " dump",
456                                            entryID);
457                 return;
458             }
459         });
460 }
461 
deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & dumpType)462 inline void deleteDumpEntry(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
463                             const std::string& entryID,
464                             const std::string& dumpType)
465 {
466     auto respHandler = [asyncResp,
467                         entryID](const boost::system::error_code& ec) {
468         BMCWEB_LOG_DEBUG("Dump Entry doDelete callback: Done");
469         if (ec)
470         {
471             if (ec.value() == EBADR)
472             {
473                 messages::resourceNotFound(asyncResp->res, "LogEntry", entryID);
474                 return;
475             }
476             BMCWEB_LOG_ERROR(
477                 "Dump (DBus) doDelete respHandler got error {} entryID={}", ec,
478                 entryID);
479             messages::internalError(asyncResp->res);
480             return;
481         }
482     };
483 
484     dbus::utility::async_method_call(
485         asyncResp, respHandler, "xyz.openbmc_project.Dump.Manager",
486         std::format("{}/entry/{}", getDumpPath(dumpType), entryID),
487         "xyz.openbmc_project.Object.Delete", "Delete");
488 }
489 
downloadDumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & entryID,const std::string & dumpType)490 inline void downloadDumpEntry(
491     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
492     const std::string& entryID, const std::string& dumpType)
493 {
494     if (dumpType != "BMC")
495     {
496         BMCWEB_LOG_WARNING("Can't find Dump Entry {}", entryID);
497         messages::resourceNotFound(asyncResp->res, dumpType + " dump", entryID);
498         return;
499     }
500 
501     std::string dumpEntryPath =
502         std::format("{}/entry/{}", getDumpPath(dumpType), entryID);
503 
504     auto downloadDumpEntryHandler =
505         [asyncResp, entryID,
506          dumpType](const boost::system::error_code& ec,
507                    const sdbusplus::message::unix_fd& unixfd) {
508             log_services_utils::downloadEntryCallback(asyncResp, entryID,
509                                                       dumpType, ec, unixfd);
510         };
511 
512     dbus::utility::async_method_call(
513         asyncResp, std::move(downloadDumpEntryHandler),
514         "xyz.openbmc_project.Dump.Manager", dumpEntryPath,
515         "xyz.openbmc_project.Dump.Entry", "GetFileHandle");
516 }
517 
mapDbusStatusToDumpProgress(const std::string & status)518 inline DumpCreationProgress mapDbusStatusToDumpProgress(
519     const std::string& status)
520 {
521     if (status ==
522             "xyz.openbmc_project.Common.Progress.OperationStatus.Failed" ||
523         status == "xyz.openbmc_project.Common.Progress.OperationStatus.Aborted")
524     {
525         return DumpCreationProgress::DUMP_CREATE_FAILED;
526     }
527     if (status ==
528         "xyz.openbmc_project.Common.Progress.OperationStatus.Completed")
529     {
530         return DumpCreationProgress::DUMP_CREATE_SUCCESS;
531     }
532     return DumpCreationProgress::DUMP_CREATE_INPROGRESS;
533 }
534 
getDumpCompletionStatus(const dbus::utility::DBusPropertiesMap & values)535 inline DumpCreationProgress getDumpCompletionStatus(
536     const dbus::utility::DBusPropertiesMap& values)
537 {
538     for (const auto& [key, val] : values)
539     {
540         if (key == "Status")
541         {
542             const std::string* value = std::get_if<std::string>(&val);
543             if (value == nullptr)
544             {
545                 BMCWEB_LOG_ERROR("Status property value is null");
546                 return DumpCreationProgress::DUMP_CREATE_FAILED;
547             }
548             return mapDbusStatusToDumpProgress(*value);
549         }
550     }
551     return DumpCreationProgress::DUMP_CREATE_INPROGRESS;
552 }
553 
getDumpEntryPath(const std::string & dumpPath)554 inline std::string getDumpEntryPath(const std::string& dumpPath)
555 {
556     if (dumpPath == "/xyz/openbmc_project/dump/bmc/entry")
557     {
558         return std::format("/redfish/v1/Managers/{}/LogServices/Dump/Entries/",
559                            BMCWEB_REDFISH_MANAGER_URI_NAME);
560     }
561     if (dumpPath == "/xyz/openbmc_project/dump/system/entry")
562     {
563         return std::format("/redfish/v1/Systems/{}/LogServices/Dump/Entries/",
564                            BMCWEB_REDFISH_SYSTEM_URI_NAME);
565     }
566     return "";
567 }
568 
createDumpTaskCallback(task::Payload && payload,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const sdbusplus::message::object_path & createdObjPath)569 inline void createDumpTaskCallback(
570     task::Payload&& payload,
571     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
572     const sdbusplus::message::object_path& createdObjPath)
573 {
574     const std::string dumpPath = createdObjPath.parent_path().str;
575     const std::string dumpId = createdObjPath.filename();
576 
577     std::string dumpEntryPath = getDumpEntryPath(dumpPath);
578 
579     if (dumpEntryPath.empty())
580     {
581         BMCWEB_LOG_ERROR("Invalid dump type received");
582         messages::internalError(asyncResp->res);
583         return;
584     }
585 
586     dbus::utility::async_method_call(
587         asyncResp,
588         [asyncResp, payload = std::move(payload), createdObjPath,
589          dumpEntryPath{std::move(dumpEntryPath)},
590          dumpId](const boost::system::error_code& ec,
591                  const std::string& introspectXml) {
592             if (ec)
593             {
594                 BMCWEB_LOG_ERROR("Introspect call failed with error: {}",
595                                  ec.message());
596                 messages::internalError(asyncResp->res);
597                 return;
598             }
599 
600             // Check if the created dump object has implemented Progress
601             // interface to track dump completion. If yes, fetch the "Status"
602             // property of the interface, modify the task state accordingly.
603             // Else, return task completed.
604             tinyxml2::XMLDocument doc;
605 
606             doc.Parse(introspectXml.data(), introspectXml.size());
607             tinyxml2::XMLNode* pRoot = doc.FirstChildElement("node");
608             if (pRoot == nullptr)
609             {
610                 BMCWEB_LOG_ERROR("XML document failed to parse");
611                 messages::internalError(asyncResp->res);
612                 return;
613             }
614             tinyxml2::XMLElement* interfaceNode =
615                 pRoot->FirstChildElement("interface");
616 
617             bool isProgressIntfPresent = false;
618             while (interfaceNode != nullptr)
619             {
620                 const char* thisInterfaceName =
621                     interfaceNode->Attribute("name");
622                 if (thisInterfaceName != nullptr)
623                 {
624                     if (thisInterfaceName ==
625                         std::string_view("xyz.openbmc_project.Common.Progress"))
626                     {
627                         interfaceNode =
628                             interfaceNode->NextSiblingElement("interface");
629                         continue;
630                     }
631                     isProgressIntfPresent = true;
632                     break;
633                 }
634                 interfaceNode = interfaceNode->NextSiblingElement("interface");
635             }
636 
637             std::shared_ptr<task::TaskData> task = task::TaskData::createTask(
638                 [createdObjPath, dumpEntryPath, dumpId, isProgressIntfPresent](
639                     const boost::system::error_code& ec2,
640                     sdbusplus::message_t& msg,
641                     const std::shared_ptr<task::TaskData>& taskData) {
642                     if (ec2)
643                     {
644                         BMCWEB_LOG_ERROR("{}: Error in creating dump",
645                                          createdObjPath.str);
646                         taskData->messages.emplace_back(
647                             messages::internalError());
648                         taskData->state = "Cancelled";
649                         return task::completed;
650                     }
651 
652                     if (isProgressIntfPresent)
653                     {
654                         dbus::utility::DBusPropertiesMap values;
655                         std::string prop;
656                         msg.read(prop, values);
657 
658                         DumpCreationProgress dumpStatus =
659                             getDumpCompletionStatus(values);
660                         if (dumpStatus ==
661                             DumpCreationProgress::DUMP_CREATE_FAILED)
662                         {
663                             BMCWEB_LOG_ERROR("{}: Error in creating dump",
664                                              createdObjPath.str);
665                             taskData->state = "Cancelled";
666                             return task::completed;
667                         }
668 
669                         if (dumpStatus ==
670                             DumpCreationProgress::DUMP_CREATE_INPROGRESS)
671                         {
672                             BMCWEB_LOG_DEBUG(
673                                 "{}: Dump creation task is in progress",
674                                 createdObjPath.str);
675                             return !task::completed;
676                         }
677                     }
678 
679                     nlohmann::json retMessage = messages::success();
680                     taskData->messages.emplace_back(retMessage);
681 
682                     boost::urls::url url = boost::urls::format(
683                         "/redfish/v1/Managers/{}/LogServices/Dump/Entries/{}",
684                         BMCWEB_REDFISH_MANAGER_URI_NAME, dumpId);
685 
686                     std::string headerLoc = "Location: ";
687                     headerLoc += url.buffer();
688 
689                     taskData->payload->httpHeaders.emplace_back(
690                         std::move(headerLoc));
691 
692                     BMCWEB_LOG_DEBUG("{}: Dump creation task completed",
693                                      createdObjPath.str);
694                     taskData->state = "Completed";
695                     return task::completed;
696                 },
697                 "type='signal',interface='org.freedesktop.DBus.Properties',"
698                 "member='PropertiesChanged',path='" +
699                     createdObjPath.str + "'");
700 
701             // The task timer is set to max time limit within which the
702             // requested dump will be collected.
703             task->startTimer(std::chrono::minutes(6));
704             task->payload.emplace(payload);
705             task->populateResp(asyncResp->res);
706         },
707         "xyz.openbmc_project.Dump.Manager", createdObjPath,
708         "org.freedesktop.DBus.Introspectable", "Introspect");
709 }
710 
createDump(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const crow::Request & req,const std::string & dumpType)711 inline void createDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
712                        const crow::Request& req, const std::string& dumpType)
713 {
714     std::string dumpPath = getDumpEntriesPath(dumpType);
715     if (dumpPath.empty())
716     {
717         messages::internalError(asyncResp->res);
718         return;
719     }
720 
721     std::optional<std::string> diagnosticDataType;
722     std::optional<std::string> oemDiagnosticDataType;
723 
724     if (!redfish::json_util::readJsonAction(               //
725             req, asyncResp->res,                           //
726             "DiagnosticDataType", diagnosticDataType,      //
727             "OEMDiagnosticDataType", oemDiagnosticDataType //
728             ))
729     {
730         return;
731     }
732 
733     if (dumpType == "System")
734     {
735         if (!oemDiagnosticDataType || !diagnosticDataType)
736         {
737             BMCWEB_LOG_ERROR(
738                 "CreateDump action parameter 'DiagnosticDataType'/'OEMDiagnosticDataType' value not found!");
739             messages::actionParameterMissing(
740                 asyncResp->res, "CollectDiagnosticData",
741                 "DiagnosticDataType & OEMDiagnosticDataType");
742             return;
743         }
744         if ((*oemDiagnosticDataType != "System") ||
745             (*diagnosticDataType != "OEM"))
746         {
747             BMCWEB_LOG_ERROR("Wrong parameter values passed");
748             messages::internalError(asyncResp->res);
749             return;
750         }
751         dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump/",
752                                BMCWEB_REDFISH_SYSTEM_URI_NAME);
753     }
754     else if (dumpType == "BMC")
755     {
756         if (!diagnosticDataType)
757         {
758             BMCWEB_LOG_ERROR(
759                 "CreateDump action parameter 'DiagnosticDataType' not found!");
760             messages::actionParameterMissing(
761                 asyncResp->res, "CollectDiagnosticData", "DiagnosticDataType");
762             return;
763         }
764         if (*diagnosticDataType != "Manager")
765         {
766             BMCWEB_LOG_ERROR(
767                 "Wrong parameter value passed for 'DiagnosticDataType'");
768             messages::internalError(asyncResp->res);
769             return;
770         }
771         dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump/",
772                                BMCWEB_REDFISH_MANAGER_URI_NAME);
773     }
774     else
775     {
776         BMCWEB_LOG_ERROR("CreateDump failed. Unknown dump type");
777         messages::internalError(asyncResp->res);
778         return;
779     }
780 
781     std::vector<std::pair<std::string, std::variant<std::string, uint64_t>>>
782         createDumpParamVec;
783 
784     if (req.session != nullptr)
785     {
786         createDumpParamVec.emplace_back(
787             "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorId",
788             req.session->clientIp);
789         createDumpParamVec.emplace_back(
790             "xyz.openbmc_project.Dump.Create.CreateParameters.OriginatorType",
791             "xyz.openbmc_project.Common.OriginatedBy.OriginatorTypes.Client");
792     }
793 
794     dbus::utility::async_method_call(
795         asyncResp,
796         [asyncResp, payload(task::Payload(req)),
797          dumpPath](const boost::system::error_code& ec,
798                    const sdbusplus::message_t& msg,
799                    const sdbusplus::message::object_path& objPath) mutable {
800             if (ec)
801             {
802                 BMCWEB_LOG_ERROR("CreateDump resp_handler got error {}", ec);
803                 const sd_bus_error* dbusError = msg.get_error();
804                 if (dbusError == nullptr)
805                 {
806                     messages::internalError(asyncResp->res);
807                     return;
808                 }
809 
810                 BMCWEB_LOG_ERROR("CreateDump DBus error: {} and error msg: {}",
811                                  dbusError->name, dbusError->message);
812                 if (std::string_view(
813                         "xyz.openbmc_project.Common.Error.NotAllowed") ==
814                     dbusError->name)
815                 {
816                     messages::resourceInStandby(asyncResp->res);
817                     return;
818                 }
819                 if (std::string_view(
820                         "xyz.openbmc_project.Dump.Create.Error.Disabled") ==
821                     dbusError->name)
822                 {
823                     messages::serviceDisabled(asyncResp->res, dumpPath);
824                     return;
825                 }
826                 if (std::string_view(
827                         "xyz.openbmc_project.Common.Error.Unavailable") ==
828                     dbusError->name)
829                 {
830                     messages::resourceInUse(asyncResp->res);
831                     return;
832                 }
833                 // Other Dbus errors such as:
834                 // xyz.openbmc_project.Common.Error.InvalidArgument &
835                 // org.freedesktop.DBus.Error.InvalidArgs are all related to
836                 // the dbus call that is made here in the bmcweb
837                 // implementation and has nothing to do with the client's
838                 // input in the request. Hence, returning internal error
839                 // back to the client.
840                 messages::internalError(asyncResp->res);
841                 return;
842             }
843             BMCWEB_LOG_DEBUG("Dump Created. Path: {}", objPath.str);
844             createDumpTaskCallback(std::move(payload), asyncResp, objPath);
845         },
846         "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType),
847         "xyz.openbmc_project.Dump.Create", "CreateDump", createDumpParamVec);
848 }
849 
clearDump(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)850 inline void clearDump(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
851                       const std::string& dumpType)
852 {
853     dbus::utility::async_method_call(
854         asyncResp,
855         [asyncResp](const boost::system::error_code& ec) {
856             if (ec)
857             {
858                 BMCWEB_LOG_ERROR("clearDump resp_handler got error {}", ec);
859                 messages::internalError(asyncResp->res);
860                 return;
861             }
862             messages::success(asyncResp->res);
863         },
864         "xyz.openbmc_project.Dump.Manager", getDumpPath(dumpType),
865         "xyz.openbmc_project.Collection.DeleteAll", "DeleteAll");
866 }
867 
parseCrashdumpParameters(const dbus::utility::DBusPropertiesMap & params,std::string & filename,std::string & timestamp,std::string & logfile)868 inline void parseCrashdumpParameters(
869     const dbus::utility::DBusPropertiesMap& params, std::string& filename,
870     std::string& timestamp, std::string& logfile)
871 {
872     const std::string* filenamePtr = nullptr;
873     const std::string* timestampPtr = nullptr;
874     const std::string* logfilePtr = nullptr;
875 
876     const bool success = sdbusplus::unpackPropertiesNoThrow(
877         dbus_utils::UnpackErrorPrinter(), params, "Timestamp", timestampPtr,
878         "Filename", filenamePtr, "Log", logfilePtr);
879 
880     if (!success)
881     {
882         return;
883     }
884 
885     if (filenamePtr != nullptr)
886     {
887         filename = *filenamePtr;
888     }
889 
890     if (timestampPtr != nullptr)
891     {
892         timestamp = *timestampPtr;
893     }
894 
895     if (logfilePtr != nullptr)
896     {
897         logfile = *logfilePtr;
898     }
899 }
900 
handleSystemsLogServiceCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)901 inline void handleSystemsLogServiceCollectionGet(
902     crow::App& app, const crow::Request& req,
903     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
904     const std::string& systemName)
905 {
906     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
907     {
908         return;
909     }
910     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
911     {
912         // Option currently returns no systems.  TBD
913         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
914                                    systemName);
915         return;
916     }
917     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
918     {
919         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
920                                    systemName);
921         return;
922     }
923 
924     // Collections don't include the static data added by SubRoute
925     // because it has a duplicate entry for members
926     asyncResp->res.jsonValue["@odata.type"] =
927         "#LogServiceCollection.LogServiceCollection";
928     asyncResp->res.jsonValue["@odata.id"] = std::format(
929         "/redfish/v1/Systems/{}/LogServices", BMCWEB_REDFISH_SYSTEM_URI_NAME);
930     asyncResp->res.jsonValue["Name"] = "System Log Services Collection";
931     asyncResp->res.jsonValue["Description"] =
932         "Collection of LogServices for this Computer System";
933     nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
934     logServiceArray = nlohmann::json::array();
935 
936     if constexpr (BMCWEB_REDFISH_EVENTLOG_LOCATION == "systems" &&
937                   !BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
938     {
939         nlohmann::json::object_t eventLog;
940         eventLog["@odata.id"] =
941             std::format("/redfish/v1/Systems/{}/LogServices/EventLog",
942                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
943         logServiceArray.emplace_back(std::move(eventLog));
944     }
945 
946     if constexpr (BMCWEB_REDFISH_DUMP_LOG)
947     {
948         nlohmann::json::object_t dumpLog;
949         dumpLog["@odata.id"] =
950             std::format("/redfish/v1/Systems/{}/LogServices/Dump",
951                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
952         logServiceArray.emplace_back(std::move(dumpLog));
953     }
954 
955     if constexpr (BMCWEB_REDFISH_CPU_LOG)
956     {
957         nlohmann::json::object_t crashdump;
958         crashdump["@odata.id"] =
959             std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
960                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
961         logServiceArray.emplace_back(std::move(crashdump));
962     }
963 
964     if constexpr (BMCWEB_REDFISH_HOST_LOGGER)
965     {
966         nlohmann::json::object_t hostlogger;
967         hostlogger["@odata.id"] =
968             std::format("/redfish/v1/Systems/{}/LogServices/HostLogger",
969                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
970         logServiceArray.emplace_back(std::move(hostlogger));
971     }
972     asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
973 
974     constexpr std::array<std::string_view, 1> interfaces = {
975         "xyz.openbmc_project.State.Boot.PostCode"};
976     dbus::utility::getSubTreePaths(
977         "/", 0, interfaces,
978         [asyncResp](
979             const boost::system::error_code& ec,
980             const dbus::utility::MapperGetSubTreePathsResponse& subtreePath) {
981             if (ec)
982             {
983                 BMCWEB_LOG_ERROR("{}", ec);
984                 return;
985             }
986 
987             for (const auto& pathStr : subtreePath)
988             {
989                 if (pathStr.find("PostCode") != std::string::npos)
990                 {
991                     nlohmann::json& logServiceArrayLocal =
992                         asyncResp->res.jsonValue["Members"];
993                     nlohmann::json::object_t member;
994                     member["@odata.id"] = std::format(
995                         "/redfish/v1/Systems/{}/LogServices/PostCodes",
996                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
997 
998                     logServiceArrayLocal.emplace_back(std::move(member));
999 
1000                     asyncResp->res.jsonValue["Members@odata.count"] =
1001                         logServiceArrayLocal.size();
1002                     return;
1003                 }
1004             }
1005         });
1006 }
1007 
handleManagersLogServicesCollectionGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1008 inline void handleManagersLogServicesCollectionGet(
1009     crow::App& app, const crow::Request& req,
1010     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1011     const std::string& managerId)
1012 {
1013     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1014     {
1015         return;
1016     }
1017 
1018     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1019     {
1020         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1021         return;
1022     }
1023 
1024     // Collections don't include the static data added by SubRoute
1025     // because it has a duplicate entry for members
1026     asyncResp->res.jsonValue["@odata.type"] =
1027         "#LogServiceCollection.LogServiceCollection";
1028     asyncResp->res.jsonValue["@odata.id"] = boost::urls::format(
1029         "/redfish/v1/Managers/{}/LogServices", BMCWEB_REDFISH_MANAGER_URI_NAME);
1030     asyncResp->res.jsonValue["Name"] = "Open BMC Log Services Collection";
1031     asyncResp->res.jsonValue["Description"] =
1032         "Collection of LogServices for this Manager";
1033     nlohmann::json& logServiceArray = asyncResp->res.jsonValue["Members"];
1034     logServiceArray = nlohmann::json::array();
1035 
1036     if constexpr (BMCWEB_REDFISH_BMC_JOURNAL)
1037     {
1038         nlohmann::json::object_t journal;
1039         journal["@odata.id"] =
1040             boost::urls::format("/redfish/v1/Managers/{}/LogServices/Journal",
1041                                 BMCWEB_REDFISH_MANAGER_URI_NAME);
1042         logServiceArray.emplace_back(std::move(journal));
1043     }
1044 
1045     if constexpr (BMCWEB_REDFISH_EVENTLOG_LOCATION == "managers")
1046     {
1047         nlohmann::json::object_t eventLog;
1048         eventLog["@odata.id"] =
1049             boost::urls::format("/redfish/v1/Managers/{}/LogServices/EventLog",
1050                                 BMCWEB_REDFISH_MANAGER_URI_NAME);
1051         logServiceArray.emplace_back(std::move(eventLog));
1052     }
1053 
1054     asyncResp->res.jsonValue["Members@odata.count"] = logServiceArray.size();
1055 
1056     if constexpr (BMCWEB_REDFISH_DUMP_LOG)
1057     {
1058         constexpr std::array<std::string_view, 1> interfaces = {
1059             "xyz.openbmc_project.Collection.DeleteAll"};
1060         dbus::utility::getSubTreePaths(
1061             "/xyz/openbmc_project/dump", 0, interfaces,
1062             [asyncResp](const boost::system::error_code& ec,
1063                         const dbus::utility::MapperGetSubTreePathsResponse&
1064                             subTreePaths) {
1065                 if (ec)
1066                 {
1067                     BMCWEB_LOG_ERROR(
1068                         "handleManagersLogServicesCollectionGet respHandler got error {}",
1069                         ec);
1070                     // Assume that getting an error simply means there are no
1071                     // dump LogServices. Return without adding any error
1072                     // response.
1073                     return;
1074                 }
1075 
1076                 nlohmann::json& logServiceArrayLocal =
1077                     asyncResp->res.jsonValue["Members"];
1078 
1079                 for (const std::string& path : subTreePaths)
1080                 {
1081                     if (path == "/xyz/openbmc_project/dump/bmc")
1082                     {
1083                         nlohmann::json::object_t member;
1084                         member["@odata.id"] = boost::urls::format(
1085                             "/redfish/v1/Managers/{}/LogServices/Dump",
1086                             BMCWEB_REDFISH_MANAGER_URI_NAME);
1087                         logServiceArrayLocal.emplace_back(std::move(member));
1088                     }
1089                     else if (path == "/xyz/openbmc_project/dump/faultlog")
1090                     {
1091                         nlohmann::json::object_t member;
1092                         member["@odata.id"] = boost::urls::format(
1093                             "/redfish/v1/Managers/{}/LogServices/FaultLog",
1094                             BMCWEB_REDFISH_MANAGER_URI_NAME);
1095                         logServiceArrayLocal.emplace_back(std::move(member));
1096                     }
1097                 }
1098 
1099                 asyncResp->res.jsonValue["Members@odata.count"] =
1100                     logServiceArrayLocal.size();
1101             });
1102     }
1103 }
1104 
handleSystemsEventLogServiceGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1105 inline void handleSystemsEventLogServiceGet(
1106     crow::App& app, const crow::Request& req,
1107     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1108     const std::string& systemName)
1109 {
1110     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1111     {
1112         return;
1113     }
1114     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1115     {
1116         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1117                                    systemName);
1118         return;
1119     }
1120     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1121     {
1122         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1123                                    systemName);
1124         return;
1125     }
1126     eventlog_utils::handleSystemsAndManagersEventLogServiceGet(
1127         asyncResp, eventlog_utils::LogServiceParent::Systems);
1128 }
1129 
handleManagersEventLogServiceGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1130 inline void handleManagersEventLogServiceGet(
1131 
1132     crow::App& app, const crow::Request& req,
1133     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1134     const std::string& managerId)
1135 {
1136     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1137     {
1138         return;
1139     }
1140     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1141     {
1142         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1143         return;
1144     }
1145     eventlog_utils::handleSystemsAndManagersEventLogServiceGet(
1146         asyncResp, eventlog_utils::LogServiceParent::Managers);
1147 }
1148 
getDumpServiceInfo(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & dumpType)1149 inline void getDumpServiceInfo(
1150     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1151     const std::string& dumpType)
1152 {
1153     std::string dumpPath;
1154     log_service::OverWritePolicy overWritePolicy =
1155         log_service::OverWritePolicy::Invalid;
1156     bool collectDiagnosticDataSupported = false;
1157 
1158     if (dumpType == "BMC")
1159     {
1160         dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/Dump",
1161                                BMCWEB_REDFISH_MANAGER_URI_NAME);
1162         overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
1163         collectDiagnosticDataSupported = true;
1164     }
1165     else if (dumpType == "FaultLog")
1166     {
1167         dumpPath = std::format("/redfish/v1/Managers/{}/LogServices/FaultLog",
1168                                BMCWEB_REDFISH_MANAGER_URI_NAME);
1169         overWritePolicy = log_service::OverWritePolicy::Unknown;
1170         collectDiagnosticDataSupported = false;
1171     }
1172     else if (dumpType == "System")
1173     {
1174         dumpPath = std::format("/redfish/v1/Systems/{}/LogServices/Dump",
1175                                BMCWEB_REDFISH_SYSTEM_URI_NAME);
1176         overWritePolicy = log_service::OverWritePolicy::WrapsWhenFull;
1177         collectDiagnosticDataSupported = true;
1178     }
1179     else
1180     {
1181         BMCWEB_LOG_ERROR("getDumpServiceInfo() invalid dump type: {}",
1182                          dumpType);
1183         messages::internalError(asyncResp->res);
1184         return;
1185     }
1186 
1187     asyncResp->res.jsonValue["@odata.id"] = dumpPath;
1188     asyncResp->res.jsonValue["@odata.type"] = "#LogService.v1_2_0.LogService";
1189     asyncResp->res.jsonValue["Name"] = "Dump LogService";
1190     asyncResp->res.jsonValue["Description"] = dumpType + " Dump LogService";
1191     asyncResp->res.jsonValue["Id"] = std::filesystem::path(dumpPath).filename();
1192     asyncResp->res.jsonValue["OverWritePolicy"] = overWritePolicy;
1193 
1194     std::pair<std::string, std::string> redfishDateTimeOffset =
1195         redfish::time_utils::getDateTimeOffsetNow();
1196     asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1197     asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1198         redfishDateTimeOffset.second;
1199 
1200     asyncResp->res.jsonValue["Entries"]["@odata.id"] = dumpPath + "/Entries";
1201 
1202     if (collectDiagnosticDataSupported)
1203     {
1204         asyncResp->res.jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
1205                                 ["target"] =
1206             dumpPath + "/Actions/LogService.CollectDiagnosticData";
1207     }
1208 
1209     etag_utils::setEtagOmitDateTimeHandler(asyncResp);
1210 
1211     constexpr std::array<std::string_view, 1> interfaces = {deleteAllInterface};
1212     dbus::utility::getSubTreePaths(
1213         "/xyz/openbmc_project/dump", 0, interfaces,
1214         [asyncResp, dumpType, dumpPath](
1215             const boost::system::error_code& ec,
1216             const dbus::utility::MapperGetSubTreePathsResponse& subTreePaths) {
1217             if (ec)
1218             {
1219                 BMCWEB_LOG_ERROR("getDumpServiceInfo respHandler got error {}",
1220                                  ec);
1221                 // Assume that getting an error simply means there are no dump
1222                 // LogServices. Return without adding any error response.
1223                 return;
1224             }
1225             std::string dbusDumpPath = getDumpPath(dumpType);
1226             for (const std::string& path : subTreePaths)
1227             {
1228                 if (path == dbusDumpPath)
1229                 {
1230                     asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
1231                                             ["target"] =
1232                         dumpPath + "/Actions/LogService.ClearLog";
1233                     break;
1234                 }
1235             }
1236         });
1237 }
1238 
handleLogServicesDumpServiceGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1239 inline void handleLogServicesDumpServiceGet(
1240     crow::App& app, const std::string& dumpType, const crow::Request& req,
1241     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1242     const std::string& managerId)
1243 {
1244     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1245     {
1246         return;
1247     }
1248 
1249     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1250     {
1251         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1252         return;
1253     }
1254 
1255     getDumpServiceInfo(asyncResp, dumpType);
1256 }
1257 
handleLogServicesDumpServiceComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)1258 inline void handleLogServicesDumpServiceComputerSystemGet(
1259     crow::App& app, const crow::Request& req,
1260     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1261     const std::string& chassisId)
1262 {
1263     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1264     {
1265         return;
1266     }
1267     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1268     {
1269         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
1270         return;
1271     }
1272     getDumpServiceInfo(asyncResp, "System");
1273 }
1274 
handleLogServicesDumpEntriesCollectionGet(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1275 inline void handleLogServicesDumpEntriesCollectionGet(
1276     crow::App& app, const std::string& dumpType, const crow::Request& req,
1277     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1278     const std::string& managerId)
1279 {
1280     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1281     {
1282         return;
1283     }
1284 
1285     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1286     {
1287         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1288         return;
1289     }
1290     getDumpEntryCollection(asyncResp, dumpType);
1291 }
1292 
handleLogServicesDumpEntriesCollectionComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId)1293 inline void handleLogServicesDumpEntriesCollectionComputerSystemGet(
1294     crow::App& app, const crow::Request& req,
1295     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1296     const std::string& chassisId)
1297 {
1298     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1299     {
1300         return;
1301     }
1302     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1303     {
1304         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
1305         return;
1306     }
1307     getDumpEntryCollection(asyncResp, "System");
1308 }
1309 
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)1310 inline void handleLogServicesDumpEntryGet(
1311     crow::App& app, const std::string& dumpType, const crow::Request& req,
1312     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1313     const std::string& managerId, const std::string& dumpId)
1314 {
1315     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1316     {
1317         return;
1318     }
1319     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1320     {
1321         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1322         return;
1323     }
1324     getDumpEntryById(asyncResp, dumpId, dumpType);
1325 }
1326 
handleLogServicesDumpEntryComputerSystemGet(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & dumpId)1327 inline void handleLogServicesDumpEntryComputerSystemGet(
1328     crow::App& app, const crow::Request& req,
1329     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1330     const std::string& chassisId, const std::string& dumpId)
1331 {
1332     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1333     {
1334         return;
1335     }
1336     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1337     {
1338         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
1339         return;
1340     }
1341     getDumpEntryById(asyncResp, dumpId, "System");
1342 }
1343 
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)1344 inline void handleLogServicesDumpEntryDelete(
1345     crow::App& app, const std::string& dumpType, const crow::Request& req,
1346     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1347     const std::string& managerId, const std::string& dumpId)
1348 {
1349     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1350     {
1351         return;
1352     }
1353 
1354     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1355     {
1356         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1357         return;
1358     }
1359     deleteDumpEntry(asyncResp, dumpId, dumpType);
1360 }
1361 
handleLogServicesDumpEntryComputerSystemDelete(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & chassisId,const std::string & dumpId)1362 inline void handleLogServicesDumpEntryComputerSystemDelete(
1363     crow::App& app, const crow::Request& req,
1364     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1365     const std::string& chassisId, const std::string& dumpId)
1366 {
1367     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1368     {
1369         return;
1370     }
1371     if (chassisId != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1372     {
1373         messages::resourceNotFound(asyncResp->res, "ComputerSystem", chassisId);
1374         return;
1375     }
1376     deleteDumpEntry(asyncResp, dumpId, "System");
1377 }
1378 
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)1379 inline void handleLogServicesDumpEntryDownloadGet(
1380     crow::App& app, const std::string& dumpType, const crow::Request& req,
1381     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1382     const std::string& managerId, const std::string& dumpId)
1383 {
1384     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1385     {
1386         return;
1387     }
1388 
1389     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1390     {
1391         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1392         return;
1393     }
1394     downloadDumpEntry(asyncResp, dumpId, dumpType);
1395 }
1396 
handleLogServicesDumpCollectDiagnosticDataPost(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1397 inline void handleLogServicesDumpCollectDiagnosticDataPost(
1398     crow::App& app, const std::string& dumpType, const crow::Request& req,
1399     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1400     const std::string& managerId)
1401 {
1402     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1403     {
1404         return;
1405     }
1406     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1407     {
1408         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1409         return;
1410     }
1411 
1412     createDump(asyncResp, req, dumpType);
1413 }
1414 
handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1415 inline void handleLogServicesDumpCollectDiagnosticDataComputerSystemPost(
1416     crow::App& app, const crow::Request& req,
1417     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1418     const std::string& systemName)
1419 {
1420     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1421     {
1422         return;
1423     }
1424 
1425     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1426     {
1427         // Option currently returns no systems.  TBD
1428         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1429                                    systemName);
1430         return;
1431     }
1432     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1433     {
1434         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1435                                    systemName);
1436         return;
1437     }
1438     createDump(asyncResp, req, "System");
1439 }
1440 
handleLogServicesDumpClearLogPost(crow::App & app,const std::string & dumpType,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & managerId)1441 inline void handleLogServicesDumpClearLogPost(
1442     crow::App& app, const std::string& dumpType, const crow::Request& req,
1443     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1444     const std::string& managerId)
1445 {
1446     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1447     {
1448         return;
1449     }
1450 
1451     if (managerId != BMCWEB_REDFISH_MANAGER_URI_NAME)
1452     {
1453         messages::resourceNotFound(asyncResp->res, "Manager", managerId);
1454         return;
1455     }
1456     clearDump(asyncResp, dumpType);
1457 }
1458 
handleLogServicesDumpClearLogComputerSystemPost(crow::App & app,const crow::Request & req,const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & systemName)1459 inline void handleLogServicesDumpClearLogComputerSystemPost(
1460     crow::App& app, const crow::Request& req,
1461     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1462     const std::string& systemName)
1463 {
1464     if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1465     {
1466         return;
1467     }
1468     if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1469     {
1470         // Option currently returns no systems.  TBD
1471         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1472                                    systemName);
1473         return;
1474     }
1475     if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1476     {
1477         messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1478                                    systemName);
1479         return;
1480     }
1481     clearDump(asyncResp, "System");
1482 }
1483 
requestRoutesBMCDumpService(App & app)1484 inline void requestRoutesBMCDumpService(App& app)
1485 {
1486     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/")
1487         .privileges(redfish::privileges::getLogService)
1488         .methods(boost::beast::http::verb::get)(std::bind_front(
1489             handleLogServicesDumpServiceGet, std::ref(app), "BMC"));
1490 }
1491 
requestRoutesBMCDumpEntryCollection(App & app)1492 inline void requestRoutesBMCDumpEntryCollection(App& app)
1493 {
1494     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/")
1495         .privileges(redfish::privileges::getLogEntryCollection)
1496         .methods(boost::beast::http::verb::get)(std::bind_front(
1497             handleLogServicesDumpEntriesCollectionGet, std::ref(app), "BMC"));
1498 }
1499 
requestRoutesBMCDumpEntry(App & app)1500 inline void requestRoutesBMCDumpEntry(App& app)
1501 {
1502     BMCWEB_ROUTE(app,
1503                  "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
1504         .privileges(redfish::privileges::getLogEntry)
1505         .methods(boost::beast::http::verb::get)(std::bind_front(
1506             handleLogServicesDumpEntryGet, std::ref(app), "BMC"));
1507 
1508     BMCWEB_ROUTE(app,
1509                  "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/")
1510         .privileges(redfish::privileges::deleteLogEntry)
1511         .methods(boost::beast::http::verb::delete_)(std::bind_front(
1512             handleLogServicesDumpEntryDelete, std::ref(app), "BMC"));
1513 }
1514 
requestRoutesBMCDumpEntryDownload(App & app)1515 inline void requestRoutesBMCDumpEntryDownload(App& app)
1516 {
1517     BMCWEB_ROUTE(
1518         app,
1519         "/redfish/v1/Managers/<str>/LogServices/Dump/Entries/<str>/attachment/")
1520         .privileges(redfish::privileges::getLogEntry)
1521         .methods(boost::beast::http::verb::get)(std::bind_front(
1522             handleLogServicesDumpEntryDownloadGet, std::ref(app), "BMC"));
1523 }
1524 
requestRoutesBMCDumpCreate(App & app)1525 inline void requestRoutesBMCDumpCreate(App& app)
1526 {
1527     BMCWEB_ROUTE(
1528         app,
1529         "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
1530         .privileges(redfish::privileges::postLogService)
1531         .methods(boost::beast::http::verb::post)(
1532             std::bind_front(handleLogServicesDumpCollectDiagnosticDataPost,
1533                             std::ref(app), "BMC"));
1534 }
1535 
requestRoutesBMCDumpClear(App & app)1536 inline void requestRoutesBMCDumpClear(App& app)
1537 {
1538     BMCWEB_ROUTE(
1539         app,
1540         "/redfish/v1/Managers/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
1541         .privileges(redfish::privileges::postLogService)
1542         .methods(boost::beast::http::verb::post)(std::bind_front(
1543             handleLogServicesDumpClearLogPost, std::ref(app), "BMC"));
1544 }
1545 
requestRoutesFaultLogDumpService(App & app)1546 inline void requestRoutesFaultLogDumpService(App& app)
1547 {
1548     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/")
1549         .privileges(redfish::privileges::getLogService)
1550         .methods(boost::beast::http::verb::get)(std::bind_front(
1551             handleLogServicesDumpServiceGet, std::ref(app), "FaultLog"));
1552 }
1553 
requestRoutesFaultLogDumpEntryCollection(App & app)1554 inline void requestRoutesFaultLogDumpEntryCollection(App& app)
1555 {
1556     BMCWEB_ROUTE(app,
1557                  "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/")
1558         .privileges(redfish::privileges::getLogEntryCollection)
1559         .methods(boost::beast::http::verb::get)(
1560             std::bind_front(handleLogServicesDumpEntriesCollectionGet,
1561                             std::ref(app), "FaultLog"));
1562 }
1563 
requestRoutesFaultLogDumpEntry(App & app)1564 inline void requestRoutesFaultLogDumpEntry(App& app)
1565 {
1566     BMCWEB_ROUTE(
1567         app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
1568         .privileges(redfish::privileges::getLogEntry)
1569         .methods(boost::beast::http::verb::get)(std::bind_front(
1570             handleLogServicesDumpEntryGet, std::ref(app), "FaultLog"));
1571 
1572     BMCWEB_ROUTE(
1573         app, "/redfish/v1/Managers/<str>/LogServices/FaultLog/Entries/<str>/")
1574         .privileges(redfish::privileges::deleteLogEntry)
1575         .methods(boost::beast::http::verb::delete_)(std::bind_front(
1576             handleLogServicesDumpEntryDelete, std::ref(app), "FaultLog"));
1577 }
1578 
requestRoutesFaultLogDumpClear(App & app)1579 inline void requestRoutesFaultLogDumpClear(App& app)
1580 {
1581     BMCWEB_ROUTE(
1582         app,
1583         "/redfish/v1/Managers/<str>/LogServices/FaultLog/Actions/LogService.ClearLog/")
1584         .privileges(redfish::privileges::postLogService)
1585         .methods(boost::beast::http::verb::post)(std::bind_front(
1586             handleLogServicesDumpClearLogPost, std::ref(app), "FaultLog"));
1587 }
1588 
requestRoutesSystemDumpService(App & app)1589 inline void requestRoutesSystemDumpService(App& app)
1590 {
1591     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/")
1592         .privileges(redfish::privileges::getLogService)
1593         .methods(boost::beast::http::verb::get)(std::bind_front(
1594             handleLogServicesDumpServiceComputerSystemGet, std::ref(app)));
1595 }
1596 
requestRoutesSystemDumpEntryCollection(App & app)1597 inline void requestRoutesSystemDumpEntryCollection(App& app)
1598 {
1599     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/")
1600         .privileges(redfish::privileges::getLogEntryCollection)
1601         .methods(boost::beast::http::verb::get)(std::bind_front(
1602             handleLogServicesDumpEntriesCollectionComputerSystemGet,
1603             std::ref(app)));
1604 }
1605 
requestRoutesSystemDumpEntry(App & app)1606 inline void requestRoutesSystemDumpEntry(App& app)
1607 {
1608     BMCWEB_ROUTE(app,
1609                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
1610         .privileges(redfish::privileges::getLogEntry)
1611         .methods(boost::beast::http::verb::get)(std::bind_front(
1612             handleLogServicesDumpEntryComputerSystemGet, std::ref(app)));
1613 
1614     BMCWEB_ROUTE(app,
1615                  "/redfish/v1/Systems/<str>/LogServices/Dump/Entries/<str>/")
1616         .privileges(redfish::privileges::deleteLogEntry)
1617         .methods(boost::beast::http::verb::delete_)(std::bind_front(
1618             handleLogServicesDumpEntryComputerSystemDelete, std::ref(app)));
1619 }
1620 
requestRoutesSystemDumpCreate(App & app)1621 inline void requestRoutesSystemDumpCreate(App& app)
1622 {
1623     BMCWEB_ROUTE(
1624         app,
1625         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.CollectDiagnosticData/")
1626         .privileges(redfish::privileges::
1627                         postLogServiceSubOverComputerSystemLogServiceCollection)
1628         .methods(boost::beast::http::verb::post)(std::bind_front(
1629             handleLogServicesDumpCollectDiagnosticDataComputerSystemPost,
1630             std::ref(app)));
1631 }
1632 
requestRoutesSystemDumpClear(App & app)1633 inline void requestRoutesSystemDumpClear(App& app)
1634 {
1635     BMCWEB_ROUTE(
1636         app,
1637         "/redfish/v1/Systems/<str>/LogServices/Dump/Actions/LogService.ClearLog/")
1638         .privileges(redfish::privileges::
1639                         postLogServiceSubOverComputerSystemLogServiceCollection)
1640         .methods(boost::beast::http::verb::post)(std::bind_front(
1641             handleLogServicesDumpClearLogComputerSystemPost, std::ref(app)));
1642 }
1643 
requestRoutesCrashdumpService(App & app)1644 inline void requestRoutesCrashdumpService(App& app)
1645 {
1646     /**
1647      * Functions triggers appropriate requests on DBus
1648      */
1649     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/")
1650         .privileges(redfish::privileges::getLogService)
1651         .methods(
1652             boost::beast::http::verb::
1653                 get)([&app](const crow::Request& req,
1654                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1655                             const std::string& systemName) {
1656             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1657             {
1658                 return;
1659             }
1660             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1661             {
1662                 // Option currently returns no systems.  TBD
1663                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1664                                            systemName);
1665                 return;
1666             }
1667             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1668             {
1669                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1670                                            systemName);
1671                 return;
1672             }
1673 
1674             // Copy over the static data to include the entries added by
1675             // SubRoute
1676             asyncResp->res.jsonValue["@odata.id"] =
1677                 std::format("/redfish/v1/Systems/{}/LogServices/Crashdump",
1678                             BMCWEB_REDFISH_SYSTEM_URI_NAME);
1679             asyncResp->res.jsonValue["@odata.type"] =
1680                 "#LogService.v1_2_0.LogService";
1681             asyncResp->res.jsonValue["Name"] = "Open BMC Oem Crashdump Service";
1682             asyncResp->res.jsonValue["Description"] = "Oem Crashdump Service";
1683             asyncResp->res.jsonValue["Id"] = "Crashdump";
1684             asyncResp->res.jsonValue["OverWritePolicy"] =
1685                 log_service::OverWritePolicy::WrapsWhenFull;
1686             asyncResp->res.jsonValue["MaxNumberOfRecords"] = 3;
1687 
1688             std::pair<std::string, std::string> redfishDateTimeOffset =
1689                 redfish::time_utils::getDateTimeOffsetNow();
1690             asyncResp->res.jsonValue["DateTime"] = redfishDateTimeOffset.first;
1691             asyncResp->res.jsonValue["DateTimeLocalOffset"] =
1692                 redfishDateTimeOffset.second;
1693 
1694             asyncResp->res.jsonValue["Entries"]["@odata.id"] = std::format(
1695                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
1696                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1697             asyncResp->res.jsonValue["Actions"]["#LogService.ClearLog"]
1698                                     ["target"] = std::format(
1699                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.ClearLog",
1700                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1701             asyncResp->res
1702                 .jsonValue["Actions"]["#LogService.CollectDiagnosticData"]
1703                           ["target"] = std::format(
1704                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData",
1705                 BMCWEB_REDFISH_SYSTEM_URI_NAME);
1706 
1707             etag_utils::setEtagOmitDateTimeHandler(asyncResp);
1708         });
1709 }
1710 
requestRoutesCrashdumpClear(App & app)1711 inline void requestRoutesCrashdumpClear(App& app)
1712 {
1713     BMCWEB_ROUTE(
1714         app,
1715         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.ClearLog/")
1716         .privileges(redfish::privileges::
1717                         postLogServiceSubOverComputerSystemLogServiceCollection)
1718         .methods(boost::beast::http::verb::post)(
1719             [&app](const crow::Request& req,
1720                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1721                    const std::string& systemName) {
1722                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1723                 {
1724                     return;
1725                 }
1726                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1727                 {
1728                     // Option currently returns no systems.  TBD
1729                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1730                                                systemName);
1731                     return;
1732                 }
1733                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1734                 {
1735                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1736                                                systemName);
1737                     return;
1738                 }
1739                 dbus::utility::async_method_call(
1740                     asyncResp,
1741                     [asyncResp](const boost::system::error_code& ec,
1742                                 const std::string&) {
1743                         if (ec)
1744                         {
1745                             messages::internalError(asyncResp->res);
1746                             return;
1747                         }
1748                         messages::success(asyncResp->res);
1749                     },
1750                     crashdumpObject, crashdumpPath, deleteAllInterface,
1751                     "DeleteAll");
1752             });
1753 }
1754 
logCrashdumpEntry(const std::shared_ptr<bmcweb::AsyncResp> & asyncResp,const std::string & logID,nlohmann::json & logEntryJson)1755 inline void logCrashdumpEntry(
1756     const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1757     const std::string& logID, nlohmann::json& logEntryJson)
1758 {
1759     auto getStoredLogCallback =
1760         [asyncResp, logID,
1761          &logEntryJson](const boost::system::error_code& ec,
1762                         const dbus::utility::DBusPropertiesMap& params) {
1763             if (ec)
1764             {
1765                 BMCWEB_LOG_DEBUG("failed to get log ec: {}", ec.message());
1766                 if (ec.value() ==
1767                     boost::system::linux_error::bad_request_descriptor)
1768                 {
1769                     messages::resourceNotFound(asyncResp->res, "LogEntry",
1770                                                logID);
1771                 }
1772                 else
1773                 {
1774                     messages::internalError(asyncResp->res);
1775                 }
1776                 return;
1777             }
1778 
1779             std::string timestamp{};
1780             std::string filename{};
1781             std::string logfile{};
1782             parseCrashdumpParameters(params, filename, timestamp, logfile);
1783 
1784             if (filename.empty() || timestamp.empty())
1785             {
1786                 messages::resourceNotFound(asyncResp->res, "LogEntry", logID);
1787                 return;
1788             }
1789 
1790             std::string crashdumpURI =
1791                 std::format(
1792                     "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/",
1793                     BMCWEB_REDFISH_SYSTEM_URI_NAME) +
1794                 logID + "/" + filename;
1795             nlohmann::json::object_t logEntry;
1796             logEntry["@odata.type"] = "#LogEntry.v1_9_0.LogEntry";
1797             logEntry["@odata.id"] = boost::urls::format(
1798                 "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries/{}",
1799                 BMCWEB_REDFISH_SYSTEM_URI_NAME, logID);
1800             logEntry["Name"] = "CPU Crashdump";
1801             logEntry["Id"] = logID;
1802             logEntry["EntryType"] = log_entry::LogEntryType::Oem;
1803             logEntry["AdditionalDataURI"] = std::move(crashdumpURI);
1804             logEntry["DiagnosticDataType"] = "OEM";
1805             logEntry["OEMDiagnosticDataType"] = "PECICrashdump";
1806             logEntry["Created"] = std::move(timestamp);
1807 
1808             // If logEntryJson references an array of LogEntry resources
1809             // ('Members' list), then push this as a new entry, otherwise set it
1810             // directly
1811             if (logEntryJson.is_array())
1812             {
1813                 logEntryJson.push_back(logEntry);
1814                 asyncResp->res.jsonValue["Members@odata.count"] =
1815                     logEntryJson.size();
1816             }
1817             else
1818             {
1819                 logEntryJson.update(logEntry);
1820             }
1821         };
1822     dbus::utility::getAllProperties(
1823         crashdumpObject, crashdumpPath + std::string("/") + logID,
1824         crashdumpInterface, std::move(getStoredLogCallback));
1825 }
1826 
requestRoutesCrashdumpEntryCollection(App & app)1827 inline void requestRoutesCrashdumpEntryCollection(App& app)
1828 {
1829     /**
1830      * Functions triggers appropriate requests on DBus
1831      */
1832     BMCWEB_ROUTE(app,
1833                  "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/")
1834         .privileges(redfish::privileges::getLogEntryCollection)
1835         .methods(
1836             boost::beast::http::verb::
1837                 get)([&app](const crow::Request& req,
1838                             const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1839                             const std::string& systemName) {
1840             if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1841             {
1842                 return;
1843             }
1844             if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1845             {
1846                 // Option currently returns no systems.  TBD
1847                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1848                                            systemName);
1849                 return;
1850             }
1851             if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1852             {
1853                 messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1854                                            systemName);
1855                 return;
1856             }
1857 
1858             constexpr std::array<std::string_view, 1> interfaces = {
1859                 crashdumpInterface};
1860             dbus::utility::getSubTreePaths(
1861                 "/", 0, interfaces,
1862                 [asyncResp](const boost::system::error_code& ec,
1863                             const std::vector<std::string>& resp) {
1864                     if (ec)
1865                     {
1866                         if (ec.value() !=
1867                             boost::system::errc::no_such_file_or_directory)
1868                         {
1869                             BMCWEB_LOG_DEBUG("failed to get entries ec: {}",
1870                                              ec.message());
1871                             messages::internalError(asyncResp->res);
1872                             return;
1873                         }
1874                     }
1875                     asyncResp->res.jsonValue["@odata.type"] =
1876                         "#LogEntryCollection.LogEntryCollection";
1877                     asyncResp->res.jsonValue["@odata.id"] = std::format(
1878                         "/redfish/v1/Systems/{}/LogServices/Crashdump/Entries",
1879                         BMCWEB_REDFISH_SYSTEM_URI_NAME);
1880                     asyncResp->res.jsonValue["Name"] =
1881                         "Open BMC Crashdump Entries";
1882                     asyncResp->res.jsonValue["Description"] =
1883                         "Collection of Crashdump Entries";
1884                     asyncResp->res.jsonValue["Members"] =
1885                         nlohmann::json::array();
1886                     asyncResp->res.jsonValue["Members@odata.count"] = 0;
1887 
1888                     for (const std::string& path : resp)
1889                     {
1890                         const sdbusplus::message::object_path objPath(path);
1891                         // Get the log ID
1892                         std::string logID = objPath.filename();
1893                         if (logID.empty())
1894                         {
1895                             continue;
1896                         }
1897                         // Add the log entry to the array
1898                         logCrashdumpEntry(asyncResp, logID,
1899                                           asyncResp->res.jsonValue["Members"]);
1900                     }
1901                 });
1902         });
1903 }
1904 
requestRoutesCrashdumpEntry(App & app)1905 inline void requestRoutesCrashdumpEntry(App& app)
1906 {
1907     BMCWEB_ROUTE(
1908         app, "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/")
1909         .privileges(redfish::privileges::getLogEntry)
1910         .methods(boost::beast::http::verb::get)(
1911             [&app](const crow::Request& req,
1912                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1913                    const std::string& systemName, const std::string& param) {
1914                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
1915                 {
1916                     return;
1917                 }
1918                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1919                 {
1920                     // Option currently returns no systems.  TBD
1921                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1922                                                systemName);
1923                     return;
1924                 }
1925                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1926                 {
1927                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1928                                                systemName);
1929                     return;
1930                 }
1931                 const std::string& logID = param;
1932                 logCrashdumpEntry(asyncResp, logID, asyncResp->res.jsonValue);
1933             });
1934 }
1935 
requestRoutesCrashdumpFile(App & app)1936 inline void requestRoutesCrashdumpFile(App& app)
1937 {
1938     BMCWEB_ROUTE(
1939         app,
1940         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Entries/<str>/<str>/")
1941         .privileges(redfish::privileges::getLogEntry)
1942         .methods(boost::beast::http::verb::get)(
1943             [](const crow::Request& req,
1944                const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
1945                const std::string& systemName, const std::string& logID,
1946                const std::string& fileName) {
1947                 // Do not call getRedfishRoute here since the crashdump file is
1948                 // not a Redfish resource.
1949 
1950                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
1951                 {
1952                     // Option currently returns no systems.  TBD
1953                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1954                                                systemName);
1955                     return;
1956                 }
1957                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
1958                 {
1959                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
1960                                                systemName);
1961                     return;
1962                 }
1963 
1964                 auto getStoredLogCallback =
1965                     [asyncResp, logID, fileName,
1966                      url(boost::urls::url(req.url()))](
1967                         const boost::system::error_code& ec,
1968                         const std::vector<std::pair<
1969                             std::string, dbus::utility::DbusVariantType>>&
1970                             resp) {
1971                         if (ec)
1972                         {
1973                             BMCWEB_LOG_DEBUG("failed to get log ec: {}",
1974                                              ec.message());
1975                             messages::internalError(asyncResp->res);
1976                             return;
1977                         }
1978 
1979                         std::string dbusFilename{};
1980                         std::string dbusTimestamp{};
1981                         std::string dbusFilepath{};
1982 
1983                         parseCrashdumpParameters(resp, dbusFilename,
1984                                                  dbusTimestamp, dbusFilepath);
1985 
1986                         if (dbusFilename.empty() || dbusTimestamp.empty() ||
1987                             dbusFilepath.empty())
1988                         {
1989                             messages::resourceNotFound(asyncResp->res,
1990                                                        "LogEntry", logID);
1991                             return;
1992                         }
1993 
1994                         // Verify the file name parameter is correct
1995                         if (fileName != dbusFilename)
1996                         {
1997                             messages::resourceNotFound(asyncResp->res,
1998                                                        "LogEntry", logID);
1999                             return;
2000                         }
2001 
2002                         if (asyncResp->res.openFile(dbusFilepath) !=
2003                             crow::OpenCode::Success)
2004                         {
2005                             messages::resourceNotFound(asyncResp->res,
2006                                                        "LogEntry", logID);
2007                             return;
2008                         }
2009 
2010                         // Configure this to be a file download when accessed
2011                         // from a browser
2012                         asyncResp->res.addHeader(
2013                             boost::beast::http::field::content_disposition,
2014                             "attachment");
2015                     };
2016                 dbus::utility::getAllProperties(
2017                     *crow::connections::systemBus, crashdumpObject,
2018                     crashdumpPath + std::string("/") + logID,
2019                     crashdumpInterface, std::move(getStoredLogCallback));
2020             });
2021 }
2022 
2023 enum class OEMDiagnosticType
2024 {
2025     onDemand,
2026     telemetry,
2027     invalid,
2028 };
2029 
getOEMDiagnosticType(std::string_view oemDiagStr)2030 inline OEMDiagnosticType getOEMDiagnosticType(std::string_view oemDiagStr)
2031 {
2032     if (oemDiagStr == "OnDemand")
2033     {
2034         return OEMDiagnosticType::onDemand;
2035     }
2036     if (oemDiagStr == "Telemetry")
2037     {
2038         return OEMDiagnosticType::telemetry;
2039     }
2040 
2041     return OEMDiagnosticType::invalid;
2042 }
2043 
requestRoutesCrashdumpCollect(App & app)2044 inline void requestRoutesCrashdumpCollect(App& app)
2045 {
2046     BMCWEB_ROUTE(
2047         app,
2048         "/redfish/v1/Systems/<str>/LogServices/Crashdump/Actions/LogService.CollectDiagnosticData/")
2049         .privileges(redfish::privileges::
2050                         postLogServiceSubOverComputerSystemLogServiceCollection)
2051         .methods(boost::beast::http::verb::post)(
2052             [&app](const crow::Request& req,
2053                    const std::shared_ptr<bmcweb::AsyncResp>& asyncResp,
2054                    const std::string& systemName) {
2055                 if (!redfish::setUpRedfishRoute(app, req, asyncResp))
2056                 {
2057                     return;
2058                 }
2059 
2060                 if constexpr (BMCWEB_EXPERIMENTAL_REDFISH_MULTI_COMPUTER_SYSTEM)
2061                 {
2062                     // Option currently returns no systems.  TBD
2063                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2064                                                systemName);
2065                     return;
2066                 }
2067                 if (systemName != BMCWEB_REDFISH_SYSTEM_URI_NAME)
2068                 {
2069                     messages::resourceNotFound(asyncResp->res, "ComputerSystem",
2070                                                systemName);
2071                     return;
2072                 }
2073 
2074                 std::string diagnosticDataType;
2075                 std::string oemDiagnosticDataType;
2076                 if (!redfish::json_util::readJsonAction(               //
2077                         req, asyncResp->res,                           //
2078                         "DiagnosticDataType", diagnosticDataType,      //
2079                         "OEMDiagnosticDataType", oemDiagnosticDataType //
2080                         ))
2081                 {
2082                     return;
2083                 }
2084 
2085                 if (diagnosticDataType != "OEM")
2086                 {
2087                     BMCWEB_LOG_ERROR(
2088                         "Only OEM DiagnosticDataType supported for Crashdump");
2089                     messages::actionParameterValueFormatError(
2090                         asyncResp->res, diagnosticDataType,
2091                         "DiagnosticDataType", "CollectDiagnosticData");
2092                     return;
2093                 }
2094 
2095                 OEMDiagnosticType oemDiagType =
2096                     getOEMDiagnosticType(oemDiagnosticDataType);
2097 
2098                 std::string iface;
2099                 std::string method;
2100                 std::string taskMatchStr;
2101                 if (oemDiagType == OEMDiagnosticType::onDemand)
2102                 {
2103                     iface = crashdumpOnDemandInterface;
2104                     method = "GenerateOnDemandLog";
2105                     taskMatchStr =
2106                         "type='signal',"
2107                         "interface='org.freedesktop.DBus.Properties',"
2108                         "member='PropertiesChanged',"
2109                         "arg0namespace='com.intel.crashdump'";
2110                 }
2111                 else if (oemDiagType == OEMDiagnosticType::telemetry)
2112                 {
2113                     iface = crashdumpTelemetryInterface;
2114                     method = "GenerateTelemetryLog";
2115                     taskMatchStr =
2116                         "type='signal',"
2117                         "interface='org.freedesktop.DBus.Properties',"
2118                         "member='PropertiesChanged',"
2119                         "arg0namespace='com.intel.crashdump'";
2120                 }
2121                 else
2122                 {
2123                     BMCWEB_LOG_ERROR("Unsupported OEMDiagnosticDataType: {}",
2124                                      oemDiagnosticDataType);
2125                     messages::actionParameterValueFormatError(
2126                         asyncResp->res, oemDiagnosticDataType,
2127                         "OEMDiagnosticDataType", "CollectDiagnosticData");
2128                     return;
2129                 }
2130 
2131                 auto collectCrashdumpCallback =
2132                     [asyncResp, payload(task::Payload(req)),
2133                      taskMatchStr](const boost::system::error_code& ec,
2134                                    const std::string&) mutable {
2135                         if (ec)
2136                         {
2137                             if (ec.value() ==
2138                                 boost::system::errc::operation_not_supported)
2139                             {
2140                                 messages::resourceInStandby(asyncResp->res);
2141                             }
2142                             else if (ec.value() == boost::system::errc::
2143                                                        device_or_resource_busy)
2144                             {
2145                                 messages::serviceTemporarilyUnavailable(
2146                                     asyncResp->res, "60");
2147                             }
2148                             else
2149                             {
2150                                 messages::internalError(asyncResp->res);
2151                             }
2152                             return;
2153                         }
2154                         std::shared_ptr<task::TaskData> task =
2155                             task::TaskData::createTask(
2156                                 [](const boost::system::error_code& ec2,
2157                                    sdbusplus::message_t&,
2158                                    const std::shared_ptr<task::TaskData>&
2159                                        taskData) {
2160                                     if (!ec2)
2161                                     {
2162                                         taskData->messages.emplace_back(
2163                                             messages::taskCompletedOK(
2164                                                 std::to_string(
2165                                                     taskData->index)));
2166                                         taskData->state = "Completed";
2167                                     }
2168                                     return task::completed;
2169                                 },
2170                                 taskMatchStr);
2171 
2172                         task->startTimer(std::chrono::minutes(5));
2173                         task->payload.emplace(std::move(payload));
2174                         task->populateResp(asyncResp->res);
2175                     };
2176 
2177                 dbus::utility::async_method_call(
2178                     asyncResp, std::move(collectCrashdumpCallback),
2179                     crashdumpObject, crashdumpPath, iface, method);
2180             });
2181 }
2182 
requestRoutesSystemsLogServiceCollection(App & app)2183 inline void requestRoutesSystemsLogServiceCollection(App& app)
2184 {
2185     /**
2186      * Functions triggers appropriate requests on DBus
2187      */
2188     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/")
2189         .privileges(redfish::privileges::getLogServiceCollection)
2190         .methods(boost::beast::http::verb::get)(std::bind_front(
2191             handleSystemsLogServiceCollectionGet, std::ref(app)));
2192 }
2193 
requestRoutesManagersLogServiceCollection(App & app)2194 inline void requestRoutesManagersLogServiceCollection(App& app)
2195 {
2196     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/")
2197         .privileges(redfish::privileges::getLogServiceCollection)
2198         .methods(boost::beast::http::verb::get)(std::bind_front(
2199             handleManagersLogServicesCollectionGet, std::ref(app)));
2200 }
2201 
requestRoutesSystemsEventLogService(App & app)2202 inline void requestRoutesSystemsEventLogService(App& app)
2203 {
2204     BMCWEB_ROUTE(app, "/redfish/v1/Systems/<str>/LogServices/EventLog/")
2205         .privileges(redfish::privileges::getLogService)
2206         .methods(boost::beast::http::verb::get)(
2207             std::bind_front(handleSystemsEventLogServiceGet, std::ref(app)));
2208 }
2209 
requestRoutesManagersEventLogService(App & app)2210 inline void requestRoutesManagersEventLogService(App& app)
2211 {
2212     BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/LogServices/EventLog/")
2213         .privileges(redfish::privileges::getLogService)
2214         .methods(boost::beast::http::verb::get)(
2215             std::bind_front(handleManagersEventLogServiceGet, std::ref(app)));
2216 }
2217 } // namespace redfish
2218