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, "Image", actionParams.imageUrl, 713 "WriteProtected", actionParams.writeProtected, "UserName", 714 actionParams.userName, "Password", actionParams.password, 715 "Inserted", actionParams.inserted, "TransferMethod", 716 actionParams.transferMethod, "TransferProtocolType", 717 actionParams.transferProtocolType)) 718 { 719 return; 720 } 721 722 dbus::utility::getDbusObject( 723 "/xyz/openbmc_project/VirtualMedia", {}, 724 [asyncResp, action, actionParams, 725 resName](const boost::system::error_code& ec, 726 const dbus::utility::MapperGetObject& getObjectType) mutable { 727 if (ec) 728 { 729 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 730 messages::resourceNotFound(asyncResp->res, action, resName); 731 732 return; 733 } 734 735 std::string service = getObjectType.begin()->first; 736 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 737 738 sdbusplus::message::object_path path( 739 "/xyz/openbmc_project/VirtualMedia"); 740 dbus::utility::getManagedObjects( 741 service, path, 742 [service, resName, action, actionParams, asyncResp]( 743 const boost::system::error_code& ec2, 744 const dbus::utility::ManagedObjectType& subtree) mutable { 745 if (ec2) 746 { 747 // Not possible in proxy mode 748 BMCWEB_LOG_DEBUG("InsertMedia not " 749 "allowed in proxy mode"); 750 messages::resourceNotFound(asyncResp->res, action, 751 resName); 752 753 return; 754 } 755 for (const auto& object : subtree) 756 { 757 VmMode mode = 758 parseObjectPathAndGetMode(object.first, resName); 759 if (mode == VmMode::Legacy) 760 { 761 validateParams(asyncResp, service, resName, 762 actionParams); 763 764 return; 765 } 766 } 767 BMCWEB_LOG_DEBUG("Parent item not found"); 768 messages::resourceNotFound(asyncResp->res, "VirtualMedia", 769 resName); 770 }); 771 }); 772 } 773 774 inline void handleManagersVirtualMediaActionEject( 775 crow::App& app, const crow::Request& req, 776 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 777 const std::string& managerName, const std::string& resName) 778 { 779 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 780 { 781 return; 782 } 783 784 constexpr std::string_view action = "VirtualMedia.EjectMedia"; 785 if (managerName != "bmc") 786 { 787 messages::resourceNotFound(asyncResp->res, action, resName); 788 789 return; 790 } 791 792 dbus::utility::getDbusObject( 793 "/xyz/openbmc_project/VirtualMedia", {}, 794 [asyncResp, action, 795 resName](const boost::system::error_code& ec2, 796 const dbus::utility::MapperGetObject& getObjectType) { 797 if (ec2) 798 { 799 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", 800 ec2); 801 messages::internalError(asyncResp->res); 802 803 return; 804 } 805 std::string service = getObjectType.begin()->first; 806 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 807 808 sdbusplus::message::object_path path( 809 "/xyz/openbmc_project/VirtualMedia"); 810 dbus::utility::getManagedObjects( 811 service, path, 812 [resName, service, action, 813 asyncResp](const boost::system::error_code& ec, 814 const dbus::utility::ManagedObjectType& subtree) { 815 if (ec) 816 { 817 BMCWEB_LOG_ERROR("ObjectMapper : No Service found"); 818 messages::resourceNotFound(asyncResp->res, action, 819 resName); 820 return; 821 } 822 823 for (const auto& object : subtree) 824 { 825 VmMode mode = 826 parseObjectPathAndGetMode(object.first, resName); 827 if (mode != VmMode::Invalid) 828 { 829 doEjectAction(asyncResp, service, resName, 830 mode == VmMode::Legacy); 831 return; 832 } 833 } 834 BMCWEB_LOG_DEBUG("Parent item not found"); 835 messages::resourceNotFound(asyncResp->res, "VirtualMedia", 836 resName); 837 }); 838 }); 839 } 840 841 inline void handleManagersVirtualMediaCollectionGet( 842 crow::App& app, const crow::Request& req, 843 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 844 const std::string& name) 845 { 846 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 847 { 848 return; 849 } 850 if (name != "bmc") 851 { 852 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name); 853 854 return; 855 } 856 857 asyncResp->res.jsonValue["@odata.type"] = 858 "#VirtualMediaCollection.VirtualMediaCollection"; 859 asyncResp->res.jsonValue["Name"] = "Virtual Media Services"; 860 asyncResp->res.jsonValue["@odata.id"] = 861 boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name); 862 863 dbus::utility::getDbusObject( 864 "/xyz/openbmc_project/VirtualMedia", {}, 865 [asyncResp, name](const boost::system::error_code& ec, 866 const dbus::utility::MapperGetObject& getObjectType) { 867 if (ec) 868 { 869 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 870 messages::internalError(asyncResp->res); 871 872 return; 873 } 874 std::string service = getObjectType.begin()->first; 875 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 876 877 getVmResourceList(asyncResp, service, name); 878 }); 879 } 880 881 inline void 882 handleVirtualMediaGet(crow::App& app, const crow::Request& req, 883 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 884 const std::string& name, const std::string& resName) 885 { 886 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 887 { 888 return; 889 } 890 if (name != "bmc") 891 { 892 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); 893 894 return; 895 } 896 897 dbus::utility::getDbusObject( 898 "/xyz/openbmc_project/VirtualMedia", {}, 899 [asyncResp, name, 900 resName](const boost::system::error_code& ec, 901 const dbus::utility::MapperGetObject& getObjectType) { 902 if (ec) 903 { 904 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 905 messages::internalError(asyncResp->res); 906 907 return; 908 } 909 std::string service = getObjectType.begin()->first; 910 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 911 912 getVmData(asyncResp, service, name, resName); 913 }); 914 } 915 916 inline void requestNBDVirtualMediaRoutes(App& app) 917 { 918 BMCWEB_ROUTE( 919 app, 920 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia") 921 .privileges(redfish::privileges::postVirtualMedia) 922 .methods(boost::beast::http::verb::post)(std::bind_front( 923 handleManagersVirtualMediaActionInsertPost, std::ref(app))); 924 925 BMCWEB_ROUTE( 926 app, 927 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia") 928 .privileges(redfish::privileges::postVirtualMedia) 929 .methods(boost::beast::http::verb::post)(std::bind_front( 930 handleManagersVirtualMediaActionEject, std::ref(app))); 931 932 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/") 933 .privileges(redfish::privileges::getVirtualMediaCollection) 934 .methods(boost::beast::http::verb::get)(std::bind_front( 935 handleManagersVirtualMediaCollectionGet, std::ref(app))); 936 937 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/") 938 .privileges(redfish::privileges::getVirtualMedia) 939 .methods(boost::beast::http::verb::get)( 940 std::bind_front(handleVirtualMediaGet, std::ref(app))); 941 } 942 943 } // namespace redfish 944