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