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