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 "account_service.hpp" 7 #include "app.hpp" 8 #include "async_resp.hpp" 9 #include "credential_pipe.hpp" 10 #include "dbus_utility.hpp" 11 #include "generated/enums/virtual_media.hpp" 12 #include "query.hpp" 13 #include "registries/privilege_registry.hpp" 14 #include "utils/json_utils.hpp" 15 16 #include <boost/url/format.hpp> 17 #include <boost/url/url_view.hpp> 18 #include <boost/url/url_view_base.hpp> 19 20 #include <array> 21 #include <ranges> 22 #include <string_view> 23 24 namespace redfish 25 { 26 27 enum class VmMode 28 { 29 Invalid, 30 Legacy, 31 Proxy 32 }; 33 34 inline VmMode parseObjectPathAndGetMode( 35 const sdbusplus::message::object_path& itemPath, const std::string& resName) 36 { 37 std::string thisPath = itemPath.filename(); 38 BMCWEB_LOG_DEBUG("Filename: {}, ThisPath: {}", itemPath.str, thisPath); 39 40 if (thisPath.empty()) 41 { 42 return VmMode::Invalid; 43 } 44 45 if (thisPath != resName) 46 { 47 return VmMode::Invalid; 48 } 49 50 auto mode = itemPath.parent_path(); 51 auto type = mode.parent_path(); 52 53 if (mode.filename().empty() || type.filename().empty()) 54 { 55 return VmMode::Invalid; 56 } 57 58 if (type.filename() != "VirtualMedia") 59 { 60 return VmMode::Invalid; 61 } 62 std::string modeStr = mode.filename(); 63 if (modeStr == "Legacy") 64 { 65 return VmMode::Legacy; 66 } 67 if (modeStr == "Proxy") 68 { 69 return VmMode::Proxy; 70 } 71 return VmMode::Invalid; 72 } 73 74 using CheckItemHandler = 75 std::function<void(const std::string& service, const std::string& resName, 76 const std::shared_ptr<bmcweb::AsyncResp>&, 77 const std::pair<sdbusplus::message::object_path, 78 dbus::utility::DBusInterfacesMap>&)>; 79 80 inline void 81 findAndParseObject(const std::string& service, const std::string& resName, 82 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 83 CheckItemHandler&& handler) 84 { 85 sdbusplus::message::object_path path("/xyz/openbmc_project/VirtualMedia"); 86 dbus::utility::getManagedObjects( 87 service, path, 88 [service, resName, asyncResp, handler = std::move(handler)]( 89 const boost::system::error_code& ec, 90 const dbus::utility::ManagedObjectType& subtree) { 91 if (ec) 92 { 93 BMCWEB_LOG_DEBUG("DBUS response error"); 94 95 return; 96 } 97 98 for (const auto& item : subtree) 99 { 100 VmMode mode = parseObjectPathAndGetMode(item.first, resName); 101 if (mode != VmMode::Invalid) 102 { 103 handler(service, resName, asyncResp, item); 104 return; 105 } 106 } 107 108 BMCWEB_LOG_DEBUG("Parent item not found"); 109 asyncResp->res.result(boost::beast::http::status::not_found); 110 }); 111 } 112 113 /** 114 * @brief Function extracts transfer protocol name from URI. 115 */ 116 inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri) 117 { 118 boost::system::result<boost::urls::url_view> url = 119 boost::urls::parse_uri(imageUri); 120 if (!url) 121 { 122 return "None"; 123 } 124 std::string_view scheme = url->scheme(); 125 if (scheme == "smb") 126 { 127 return "CIFS"; 128 } 129 if (scheme == "https") 130 { 131 return "HTTPS"; 132 } 133 134 return "None"; 135 } 136 137 /** 138 * @brief Read all known properties from VM object interfaces 139 */ 140 inline void 141 vmParseInterfaceObject(const dbus::utility::DBusInterfacesMap& interfaces, 142 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 143 { 144 for (const auto& [interface, values] : interfaces) 145 { 146 if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint") 147 { 148 for (const auto& [property, value] : values) 149 { 150 if (property == "EndpointId") 151 { 152 const std::string* endpointIdValue = 153 std::get_if<std::string>(&value); 154 if (endpointIdValue == nullptr) 155 { 156 continue; 157 } 158 if (!endpointIdValue->empty()) 159 { 160 // Proxy mode 161 asyncResp->res 162 .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] = 163 *endpointIdValue; 164 asyncResp->res.jsonValue["TransferProtocolType"] = 165 "OEM"; 166 } 167 } 168 if (property == "ImageURL") 169 { 170 const std::string* imageUrlValue = 171 std::get_if<std::string>(&value); 172 if (imageUrlValue != nullptr && !imageUrlValue->empty()) 173 { 174 std::filesystem::path filePath = *imageUrlValue; 175 if (!filePath.has_filename()) 176 { 177 // this will handle https share, which not 178 // necessarily has to have filename given. 179 asyncResp->res.jsonValue["ImageName"] = ""; 180 } 181 else 182 { 183 asyncResp->res.jsonValue["ImageName"] = 184 filePath.filename(); 185 } 186 187 asyncResp->res.jsonValue["Image"] = *imageUrlValue; 188 asyncResp->res.jsonValue["TransferProtocolType"] = 189 getTransferProtocolTypeFromUri(*imageUrlValue); 190 191 asyncResp->res.jsonValue["ConnectedVia"] = 192 virtual_media::ConnectedVia::URI; 193 } 194 } 195 if (property == "WriteProtected") 196 { 197 const bool* writeProtectedValue = std::get_if<bool>(&value); 198 if (writeProtectedValue != nullptr) 199 { 200 asyncResp->res.jsonValue["WriteProtected"] = 201 *writeProtectedValue; 202 } 203 } 204 } 205 } 206 if (interface == "xyz.openbmc_project.VirtualMedia.Process") 207 { 208 for (const auto& [property, value] : values) 209 { 210 if (property == "Active") 211 { 212 const bool* activeValue = std::get_if<bool>(&value); 213 if (activeValue == nullptr) 214 { 215 BMCWEB_LOG_DEBUG("Value Active not found"); 216 return; 217 } 218 asyncResp->res.jsonValue["Inserted"] = *activeValue; 219 220 if (*activeValue) 221 { 222 asyncResp->res.jsonValue["ConnectedVia"] = 223 virtual_media::ConnectedVia::Applet; 224 } 225 } 226 } 227 } 228 } 229 } 230 231 /** 232 * @brief Fill template for Virtual Media Item. 233 */ 234 inline nlohmann::json vmItemTemplate(const std::string& name, 235 const std::string& resName) 236 { 237 nlohmann::json item; 238 item["@odata.id"] = boost::urls::format( 239 "/redfish/v1/Managers/{}/VirtualMedia/{}", name, resName); 240 241 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia"; 242 item["Name"] = "Virtual Removable Media"; 243 item["Id"] = resName; 244 item["WriteProtected"] = true; 245 item["ConnectedVia"] = virtual_media::ConnectedVia::NotConnected; 246 item["MediaTypes"] = nlohmann::json::array_t({"CD", "USBStick"}); 247 item["TransferMethod"] = virtual_media::TransferMethod::Stream; 248 item["Oem"]["OpenBMC"]["@odata.type"] = 249 "#OpenBMCVirtualMedia.v1_0_0.VirtualMedia"; 250 item["Oem"]["OpenBMC"]["@odata.id"] = boost::urls::format( 251 "/redfish/v1/Managers/{}/VirtualMedia/{}#/Oem/OpenBMC", name, resName); 252 253 return item; 254 } 255 256 /** 257 * @brief Fills collection data 258 */ 259 inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 260 const std::string& service, 261 const std::string& name) 262 { 263 BMCWEB_LOG_DEBUG("Get available Virtual Media resources."); 264 sdbusplus::message::object_path objPath( 265 "/xyz/openbmc_project/VirtualMedia"); 266 dbus::utility::getManagedObjects( 267 service, objPath, 268 [name, asyncResp{std::move(asyncResp)}]( 269 const boost::system::error_code& ec, 270 const dbus::utility::ManagedObjectType& subtree) { 271 if (ec) 272 { 273 BMCWEB_LOG_DEBUG("DBUS response error"); 274 return; 275 } 276 nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 277 members = nlohmann::json::array(); 278 279 for (const auto& object : subtree) 280 { 281 nlohmann::json item; 282 std::string path = object.first.filename(); 283 if (path.empty()) 284 { 285 continue; 286 } 287 288 item["@odata.id"] = boost::urls::format( 289 "/redfish/v1/Managers/{}/VirtualMedia/{}", name, path); 290 members.emplace_back(std::move(item)); 291 } 292 asyncResp->res.jsonValue["Members@odata.count"] = members.size(); 293 }); 294 } 295 296 inline void 297 afterGetVmData(const std::string& name, const std::string& /*service*/, 298 const std::string& resName, 299 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 300 const std::pair<sdbusplus::message::object_path, 301 dbus::utility::DBusInterfacesMap>& item) 302 { 303 VmMode mode = parseObjectPathAndGetMode(item.first, resName); 304 if (mode == VmMode::Invalid) 305 { 306 return; 307 } 308 309 asyncResp->res.jsonValue = vmItemTemplate(name, resName); 310 311 // Check if dbus path is Legacy type 312 if (mode == VmMode::Legacy) 313 { 314 asyncResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"] 315 ["target"] = boost::urls::format( 316 "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.InsertMedia", 317 name, resName); 318 } 319 320 vmParseInterfaceObject(item.second, asyncResp); 321 322 asyncResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"] 323 ["target"] = boost::urls::format( 324 "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.EjectMedia", 325 name, resName); 326 } 327 328 /** 329 * @brief Fills data for specific resource 330 */ 331 inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 332 const std::string& service, const std::string& name, 333 const std::string& resName) 334 { 335 BMCWEB_LOG_DEBUG("Get Virtual Media resource data."); 336 337 findAndParseObject(service, resName, asyncResp, 338 std::bind_front(afterGetVmData, name)); 339 } 340 341 /** 342 * @brief Transfer protocols supported for InsertMedia action. 343 * 344 */ 345 enum class TransferProtocol 346 { 347 https, 348 smb, 349 invalid 350 }; 351 352 /** 353 * @brief Function extracts transfer protocol type from URI. 354 * 355 */ 356 inline std::optional<TransferProtocol> 357 getTransferProtocolFromUri(const boost::urls::url_view_base& imageUri) 358 { 359 std::string_view scheme = imageUri.scheme(); 360 if (scheme == "smb") 361 { 362 return TransferProtocol::smb; 363 } 364 if (scheme == "https") 365 { 366 return TransferProtocol::https; 367 } 368 if (!scheme.empty()) 369 { 370 return TransferProtocol::invalid; 371 } 372 373 return {}; 374 } 375 376 /** 377 * @brief Function convert transfer protocol from string param. 378 * 379 */ 380 inline std::optional<TransferProtocol> getTransferProtocolFromParam( 381 const std::optional<std::string>& transferProtocolType) 382 { 383 if (!transferProtocolType) 384 { 385 return {}; 386 } 387 388 if (*transferProtocolType == "CIFS") 389 { 390 return TransferProtocol::smb; 391 } 392 393 if (*transferProtocolType == "HTTPS") 394 { 395 return TransferProtocol::https; 396 } 397 398 return TransferProtocol::invalid; 399 } 400 401 /** 402 * @brief Function extends URI with transfer protocol type. 403 * 404 */ 405 inline std::string getUriWithTransferProtocol( 406 const std::string& imageUri, const TransferProtocol& transferProtocol) 407 { 408 if (transferProtocol == TransferProtocol::smb) 409 { 410 return "smb://" + imageUri; 411 } 412 413 if (transferProtocol == TransferProtocol::https) 414 { 415 return "https://" + imageUri; 416 } 417 418 return imageUri; 419 } 420 421 struct InsertMediaActionParams 422 { 423 std::optional<std::string> imageUrl; 424 std::optional<std::string> userName; 425 std::optional<std::string> password; 426 std::optional<std::string> transferMethod; 427 std::optional<std::string> transferProtocolType; 428 std::optional<bool> writeProtected = true; 429 std::optional<bool> inserted; 430 }; 431 432 /** 433 * @brief Function transceives data with dbus directly. 434 * 435 * All BMC state properties will be retrieved before sending reset request. 436 */ 437 inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 438 const std::string& service, const std::string& name, 439 const std::string& imageUrl, bool rw, 440 std::string&& userName, std::string&& password) 441 { 442 int fd = -1; 443 std::shared_ptr<CredentialsPipe> secretPipe; 444 if (!userName.empty() || !password.empty()) 445 { 446 // Payload must contain data + NULL delimiters 447 constexpr const size_t secretLimit = 1024; 448 if (userName.size() + password.size() + 2 > secretLimit) 449 { 450 BMCWEB_LOG_ERROR("Credentials too long to handle"); 451 messages::unrecognizedRequestBody(asyncResp->res); 452 return; 453 } 454 455 // Open pipe 456 secretPipe = std::make_shared<CredentialsPipe>( 457 crow::connections::systemBus->get_io_context()); 458 fd = secretPipe->releaseFd(); 459 460 // Pass secret over pipe 461 secretPipe->asyncWrite( 462 std::move(userName), std::move(password), 463 [asyncResp, 464 secretPipe](const boost::system::error_code& ec, std::size_t) { 465 if (ec) 466 { 467 BMCWEB_LOG_ERROR("Failed to pass secret: {}", ec); 468 messages::internalError(asyncResp->res); 469 } 470 }); 471 } 472 473 std::variant<sdbusplus::message::unix_fd> unixFd( 474 std::in_place_type<sdbusplus::message::unix_fd>, fd); 475 476 sdbusplus::message::object_path path( 477 "/xyz/openbmc_project/VirtualMedia/Legacy"); 478 path /= name; 479 crow::connections::systemBus->async_method_call( 480 [asyncResp, 481 secretPipe](const boost::system::error_code& ec, bool success) { 482 if (ec) 483 { 484 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); 485 messages::internalError(asyncResp->res); 486 return; 487 } 488 if (!success) 489 { 490 BMCWEB_LOG_ERROR("Service responded with error"); 491 messages::internalError(asyncResp->res); 492 } 493 }, 494 service, path.str, "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", 495 imageUrl, rw, unixFd); 496 } 497 498 /** 499 * @brief Function validate parameters of insert media request. 500 * 501 */ 502 inline void validateParams(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 503 const std::string& service, 504 const std::string& resName, 505 InsertMediaActionParams& actionParams) 506 { 507 BMCWEB_LOG_DEBUG("Validation started"); 508 // required param imageUrl must not be empty 509 if (!actionParams.imageUrl) 510 { 511 BMCWEB_LOG_ERROR("Request action parameter Image is empty."); 512 513 messages::propertyValueFormatError(asyncResp->res, "<empty>", "Image"); 514 515 return; 516 } 517 518 // optional param inserted must be true 519 if (actionParams.inserted && !*actionParams.inserted) 520 { 521 BMCWEB_LOG_ERROR( 522 "Request action optional parameter Inserted must be true."); 523 524 messages::actionParameterNotSupported(asyncResp->res, "Inserted", 525 "InsertMedia"); 526 527 return; 528 } 529 530 // optional param transferMethod must be stream 531 if (actionParams.transferMethod && 532 (*actionParams.transferMethod != "Stream")) 533 { 534 BMCWEB_LOG_ERROR("Request action optional parameter " 535 "TransferMethod must be Stream."); 536 537 messages::actionParameterNotSupported(asyncResp->res, "TransferMethod", 538 "InsertMedia"); 539 540 return; 541 } 542 boost::system::result<boost::urls::url_view> url = 543 boost::urls::parse_uri(*actionParams.imageUrl); 544 if (!url) 545 { 546 messages::actionParameterValueFormatError( 547 asyncResp->res, *actionParams.imageUrl, "Image", "InsertMedia"); 548 return; 549 } 550 std::optional<TransferProtocol> uriTransferProtocolType = 551 getTransferProtocolFromUri(*url); 552 553 std::optional<TransferProtocol> paramTransferProtocolType = 554 getTransferProtocolFromParam(actionParams.transferProtocolType); 555 556 // ImageUrl does not contain valid protocol type 557 if (uriTransferProtocolType && 558 *uriTransferProtocolType == TransferProtocol::invalid) 559 { 560 BMCWEB_LOG_ERROR("Request action parameter ImageUrl must " 561 "contain specified protocol type from list: " 562 "(smb, https)."); 563 564 messages::resourceAtUriInUnknownFormat(asyncResp->res, *url); 565 566 return; 567 } 568 569 // transferProtocolType should contain value from list 570 if (paramTransferProtocolType && 571 *paramTransferProtocolType == TransferProtocol::invalid) 572 { 573 BMCWEB_LOG_ERROR("Request action parameter TransferProtocolType " 574 "must be provided with value from list: " 575 "(CIFS, HTTPS)."); 576 577 messages::propertyValueNotInList( 578 asyncResp->res, actionParams.transferProtocolType.value_or(""), 579 "TransferProtocolType"); 580 return; 581 } 582 583 // valid transfer protocol not provided either with URI nor param 584 if (!uriTransferProtocolType && !paramTransferProtocolType) 585 { 586 BMCWEB_LOG_ERROR("Request action parameter ImageUrl must " 587 "contain specified protocol type or param " 588 "TransferProtocolType must be provided."); 589 590 messages::resourceAtUriInUnknownFormat(asyncResp->res, *url); 591 592 return; 593 } 594 595 // valid transfer protocol provided both with URI and param 596 if (paramTransferProtocolType && uriTransferProtocolType) 597 { 598 // check if protocol is the same for URI and param 599 if (*paramTransferProtocolType != *uriTransferProtocolType) 600 { 601 BMCWEB_LOG_ERROR("Request action parameter " 602 "TransferProtocolType must contain the " 603 "same protocol type as protocol type " 604 "provided with param imageUrl."); 605 606 messages::actionParameterValueTypeError( 607 asyncResp->res, actionParams.transferProtocolType.value_or(""), 608 "TransferProtocolType", "InsertMedia"); 609 610 return; 611 } 612 } 613 614 // validation passed, add protocol to URI if needed 615 if (!uriTransferProtocolType && paramTransferProtocolType) 616 { 617 actionParams.imageUrl = getUriWithTransferProtocol( 618 *actionParams.imageUrl, *paramTransferProtocolType); 619 } 620 621 if (!actionParams.userName) 622 { 623 actionParams.userName = ""; 624 } 625 626 if (!actionParams.password) 627 { 628 actionParams.password = ""; 629 } 630 631 doMountVmLegacy(asyncResp, service, resName, *actionParams.imageUrl, 632 !(actionParams.writeProtected.value_or(false)), 633 std::move(*actionParams.userName), 634 std::move(*actionParams.password)); 635 } 636 637 /** 638 * @brief Function transceives data with dbus directly. 639 * 640 * All BMC state properties will be retrieved before sending reset request. 641 */ 642 inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 643 const std::string& service, const std::string& name, 644 bool legacy) 645 { 646 // Legacy mount requires parameter with image 647 if (legacy) 648 { 649 crow::connections::systemBus->async_method_call( 650 [asyncResp](const boost::system::error_code& ec) { 651 if (ec) 652 { 653 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); 654 655 messages::internalError(asyncResp->res); 656 return; 657 } 658 }, 659 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, 660 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount"); 661 } 662 else // proxy 663 { 664 crow::connections::systemBus->async_method_call( 665 [asyncResp](const boost::system::error_code& ec) { 666 if (ec) 667 { 668 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); 669 670 messages::internalError(asyncResp->res); 671 return; 672 } 673 }, 674 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, 675 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); 676 } 677 } 678 679 inline void handleManagersVirtualMediaActionInsertPost( 680 crow::App& app, const crow::Request& req, 681 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 682 const std::string& name, const std::string& resName) 683 { 684 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 685 { 686 return; 687 } 688 689 constexpr std::string_view action = "VirtualMedia.InsertMedia"; 690 if (name != "bmc") 691 { 692 messages::resourceNotFound(asyncResp->res, action, resName); 693 694 return; 695 } 696 InsertMediaActionParams actionParams; 697 698 // Read obligatory parameters (url of image) 699 if (!json_util::readJsonAction( // 700 req, asyncResp->res, // 701 "Image", actionParams.imageUrl, // 702 "Inserted", actionParams.inserted, // 703 "Password", actionParams.password, // 704 "TransferMethod", actionParams.transferMethod, // 705 "TransferProtocolType", actionParams.transferProtocolType, // 706 "UserName", actionParams.userName, // 707 "WriteProtected", actionParams.writeProtected // 708 )) 709 { 710 return; 711 } 712 713 dbus::utility::getDbusObject( 714 "/xyz/openbmc_project/VirtualMedia", {}, 715 [asyncResp, action, actionParams, 716 resName](const boost::system::error_code& ec, 717 const dbus::utility::MapperGetObject& getObjectType) mutable { 718 if (ec) 719 { 720 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 721 messages::resourceNotFound(asyncResp->res, action, resName); 722 723 return; 724 } 725 726 std::string service = getObjectType.begin()->first; 727 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 728 729 sdbusplus::message::object_path path( 730 "/xyz/openbmc_project/VirtualMedia"); 731 dbus::utility::getManagedObjects( 732 service, path, 733 [service, resName, action, actionParams, asyncResp]( 734 const boost::system::error_code& ec2, 735 const dbus::utility::ManagedObjectType& subtree) mutable { 736 if (ec2) 737 { 738 // Not possible in proxy mode 739 BMCWEB_LOG_DEBUG("InsertMedia not " 740 "allowed in proxy mode"); 741 messages::resourceNotFound(asyncResp->res, action, 742 resName); 743 744 return; 745 } 746 for (const auto& object : subtree) 747 { 748 VmMode mode = 749 parseObjectPathAndGetMode(object.first, resName); 750 if (mode == VmMode::Legacy) 751 { 752 validateParams(asyncResp, service, resName, 753 actionParams); 754 755 return; 756 } 757 } 758 BMCWEB_LOG_DEBUG("Parent item not found"); 759 messages::resourceNotFound(asyncResp->res, "VirtualMedia", 760 resName); 761 }); 762 }); 763 } 764 765 inline void handleManagersVirtualMediaActionEject( 766 crow::App& app, const crow::Request& req, 767 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 768 const std::string& managerName, const std::string& resName) 769 { 770 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 771 { 772 return; 773 } 774 775 constexpr std::string_view action = "VirtualMedia.EjectMedia"; 776 if (managerName != "bmc") 777 { 778 messages::resourceNotFound(asyncResp->res, action, resName); 779 780 return; 781 } 782 783 dbus::utility::getDbusObject( 784 "/xyz/openbmc_project/VirtualMedia", {}, 785 [asyncResp, action, 786 resName](const boost::system::error_code& ec2, 787 const dbus::utility::MapperGetObject& getObjectType) { 788 if (ec2) 789 { 790 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", 791 ec2); 792 messages::internalError(asyncResp->res); 793 794 return; 795 } 796 std::string service = getObjectType.begin()->first; 797 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 798 799 sdbusplus::message::object_path path( 800 "/xyz/openbmc_project/VirtualMedia"); 801 dbus::utility::getManagedObjects( 802 service, path, 803 [resName, service, action, 804 asyncResp](const boost::system::error_code& ec, 805 const dbus::utility::ManagedObjectType& subtree) { 806 if (ec) 807 { 808 BMCWEB_LOG_ERROR("ObjectMapper : No Service found"); 809 messages::resourceNotFound(asyncResp->res, action, 810 resName); 811 return; 812 } 813 814 for (const auto& object : subtree) 815 { 816 VmMode mode = 817 parseObjectPathAndGetMode(object.first, resName); 818 if (mode != VmMode::Invalid) 819 { 820 doEjectAction(asyncResp, service, resName, 821 mode == VmMode::Legacy); 822 return; 823 } 824 } 825 BMCWEB_LOG_DEBUG("Parent item not found"); 826 messages::resourceNotFound(asyncResp->res, "VirtualMedia", 827 resName); 828 }); 829 }); 830 } 831 832 inline void handleManagersVirtualMediaCollectionGet( 833 crow::App& app, const crow::Request& req, 834 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 835 const std::string& name) 836 { 837 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 838 { 839 return; 840 } 841 if (name != "bmc") 842 { 843 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name); 844 845 return; 846 } 847 848 asyncResp->res.jsonValue["@odata.type"] = 849 "#VirtualMediaCollection.VirtualMediaCollection"; 850 asyncResp->res.jsonValue["Name"] = "Virtual Media Services"; 851 asyncResp->res.jsonValue["@odata.id"] = 852 boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name); 853 854 dbus::utility::getDbusObject( 855 "/xyz/openbmc_project/VirtualMedia", {}, 856 [asyncResp, name](const boost::system::error_code& ec, 857 const dbus::utility::MapperGetObject& getObjectType) { 858 if (ec) 859 { 860 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 861 messages::internalError(asyncResp->res); 862 863 return; 864 } 865 std::string service = getObjectType.begin()->first; 866 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 867 868 getVmResourceList(asyncResp, service, name); 869 }); 870 } 871 872 inline void 873 handleVirtualMediaGet(crow::App& app, const crow::Request& req, 874 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 875 const std::string& name, const std::string& resName) 876 { 877 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 878 { 879 return; 880 } 881 if (name != "bmc") 882 { 883 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); 884 885 return; 886 } 887 888 dbus::utility::getDbusObject( 889 "/xyz/openbmc_project/VirtualMedia", {}, 890 [asyncResp, name, 891 resName](const boost::system::error_code& ec, 892 const dbus::utility::MapperGetObject& getObjectType) { 893 if (ec) 894 { 895 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 896 messages::internalError(asyncResp->res); 897 898 return; 899 } 900 std::string service = getObjectType.begin()->first; 901 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 902 903 getVmData(asyncResp, service, name, resName); 904 }); 905 } 906 907 inline void requestNBDVirtualMediaRoutes(App& app) 908 { 909 BMCWEB_ROUTE( 910 app, 911 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia") 912 .privileges(redfish::privileges::postVirtualMedia) 913 .methods(boost::beast::http::verb::post)(std::bind_front( 914 handleManagersVirtualMediaActionInsertPost, std::ref(app))); 915 916 BMCWEB_ROUTE( 917 app, 918 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia") 919 .privileges(redfish::privileges::postVirtualMedia) 920 .methods(boost::beast::http::verb::post)(std::bind_front( 921 handleManagersVirtualMediaActionEject, std::ref(app))); 922 923 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/") 924 .privileges(redfish::privileges::getVirtualMediaCollection) 925 .methods(boost::beast::http::verb::get)(std::bind_front( 926 handleManagersVirtualMediaCollectionGet, std::ref(app))); 927 928 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/") 929 .privileges(redfish::privileges::getVirtualMedia) 930 .methods(boost::beast::http::verb::get)( 931 std::bind_front(handleVirtualMediaGet, std::ref(app))); 932 } 933 934 } // namespace redfish 935