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