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