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