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