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