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