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 #include <boost/url/url_view_base.hpp> 32 33 #include <array> 34 #include <ranges> 35 #include <string_view> 36 37 namespace redfish 38 { 39 40 enum class VmMode 41 { 42 Invalid, 43 Legacy, 44 Proxy 45 }; 46 47 inline VmMode 48 parseObjectPathAndGetMode(const sdbusplus::message::object_path& itemPath, 49 const std::string& resName) 50 { 51 std::string thisPath = itemPath.filename(); 52 BMCWEB_LOG_DEBUG("Filename: {}, ThisPath: {}", itemPath.str, thisPath); 53 54 if (thisPath.empty()) 55 { 56 return VmMode::Invalid; 57 } 58 59 if (thisPath != resName) 60 { 61 return VmMode::Invalid; 62 } 63 64 auto mode = itemPath.parent_path(); 65 auto type = mode.parent_path(); 66 67 if (mode.filename().empty() || type.filename().empty()) 68 { 69 return VmMode::Invalid; 70 } 71 72 if (type.filename() != "VirtualMedia") 73 { 74 return VmMode::Invalid; 75 } 76 std::string modeStr = mode.filename(); 77 if (modeStr == "Legacy") 78 { 79 return VmMode::Legacy; 80 } 81 if (modeStr == "Proxy") 82 { 83 return VmMode::Proxy; 84 } 85 return VmMode::Invalid; 86 } 87 88 using CheckItemHandler = 89 std::function<void(const std::string& service, const std::string& resName, 90 const std::shared_ptr<bmcweb::AsyncResp>&, 91 const std::pair<sdbusplus::message::object_path, 92 dbus::utility::DBusInterfacesMap>&)>; 93 94 inline void 95 findAndParseObject(const std::string& service, const std::string& resName, 96 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 97 CheckItemHandler&& handler) 98 { 99 sdbusplus::message::object_path path("/xyz/openbmc_project/VirtualMedia"); 100 dbus::utility::getManagedObjects( 101 service, path, 102 [service, resName, asyncResp, handler = std::move(handler)]( 103 const boost::system::error_code& ec, 104 const dbus::utility::ManagedObjectType& subtree) { 105 if (ec) 106 { 107 BMCWEB_LOG_DEBUG("DBUS response error"); 108 109 return; 110 } 111 112 for (const auto& item : subtree) 113 { 114 VmMode mode = parseObjectPathAndGetMode(item.first, resName); 115 if (mode != VmMode::Invalid) 116 { 117 handler(service, resName, asyncResp, item); 118 return; 119 } 120 } 121 122 BMCWEB_LOG_DEBUG("Parent item not found"); 123 asyncResp->res.result(boost::beast::http::status::not_found); 124 }); 125 } 126 127 /** 128 * @brief Function extracts transfer protocol name from URI. 129 */ 130 inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri) 131 { 132 boost::system::result<boost::urls::url_view> url = 133 boost::urls::parse_uri(imageUri); 134 if (!url) 135 { 136 return "None"; 137 } 138 std::string_view scheme = url->scheme(); 139 if (scheme == "smb") 140 { 141 return "CIFS"; 142 } 143 if (scheme == "https") 144 { 145 return "HTTPS"; 146 } 147 148 return "None"; 149 } 150 151 /** 152 * @brief Read all known properties from VM object interfaces 153 */ 154 inline void 155 vmParseInterfaceObject(const dbus::utility::DBusInterfacesMap& interfaces, 156 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp) 157 { 158 for (const auto& [interface, values] : interfaces) 159 { 160 if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint") 161 { 162 for (const auto& [property, value] : values) 163 { 164 if (property == "EndpointId") 165 { 166 const std::string* endpointIdValue = 167 std::get_if<std::string>(&value); 168 if (endpointIdValue == nullptr) 169 { 170 continue; 171 } 172 if (!endpointIdValue->empty()) 173 { 174 // Proxy mode 175 asyncResp->res 176 .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] = 177 *endpointIdValue; 178 asyncResp->res.jsonValue["TransferProtocolType"] = 179 "OEM"; 180 } 181 } 182 if (property == "ImageURL") 183 { 184 const std::string* imageUrlValue = 185 std::get_if<std::string>(&value); 186 if (imageUrlValue != nullptr && !imageUrlValue->empty()) 187 { 188 std::filesystem::path filePath = *imageUrlValue; 189 if (!filePath.has_filename()) 190 { 191 // this will handle https share, which not 192 // necessarily has to have filename given. 193 asyncResp->res.jsonValue["ImageName"] = ""; 194 } 195 else 196 { 197 asyncResp->res.jsonValue["ImageName"] = 198 filePath.filename(); 199 } 200 201 asyncResp->res.jsonValue["Image"] = *imageUrlValue; 202 asyncResp->res.jsonValue["TransferProtocolType"] = 203 getTransferProtocolTypeFromUri(*imageUrlValue); 204 205 asyncResp->res.jsonValue["ConnectedVia"] = 206 virtual_media::ConnectedVia::URI; 207 } 208 } 209 if (property == "WriteProtected") 210 { 211 const bool* writeProtectedValue = std::get_if<bool>(&value); 212 if (writeProtectedValue != nullptr) 213 { 214 asyncResp->res.jsonValue["WriteProtected"] = 215 *writeProtectedValue; 216 } 217 } 218 } 219 } 220 if (interface == "xyz.openbmc_project.VirtualMedia.Process") 221 { 222 for (const auto& [property, value] : values) 223 { 224 if (property == "Active") 225 { 226 const bool* activeValue = std::get_if<bool>(&value); 227 if (activeValue == nullptr) 228 { 229 BMCWEB_LOG_DEBUG("Value Active not found"); 230 return; 231 } 232 asyncResp->res.jsonValue["Inserted"] = *activeValue; 233 234 if (*activeValue) 235 { 236 asyncResp->res.jsonValue["ConnectedVia"] = 237 virtual_media::ConnectedVia::Applet; 238 } 239 } 240 } 241 } 242 } 243 } 244 245 /** 246 * @brief Fill template for Virtual Media Item. 247 */ 248 inline nlohmann::json vmItemTemplate(const std::string& name, 249 const std::string& resName) 250 { 251 nlohmann::json item; 252 item["@odata.id"] = boost::urls::format( 253 "/redfish/v1/Managers/{}/VirtualMedia/{}", name, resName); 254 255 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia"; 256 item["Name"] = "Virtual Removable Media"; 257 item["Id"] = resName; 258 item["WriteProtected"] = true; 259 item["ConnectedVia"] = virtual_media::ConnectedVia::NotConnected; 260 item["MediaTypes"] = nlohmann::json::array_t({"CD", "USBStick"}); 261 item["TransferMethod"] = "Stream"; 262 item["Oem"]["OpenBMC"]["@odata.type"] = 263 "#OemVirtualMedia.v1_0_0.VirtualMedia"; 264 item["Oem"]["OpenBMC"]["@odata.id"] = boost::urls::format( 265 "/redfish/v1/Managers/{}/VirtualMedia/{}#/Oem/OpenBMC", name, resName); 266 267 return item; 268 } 269 270 /** 271 * @brief Fills collection data 272 */ 273 inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> asyncResp, 274 const std::string& service, 275 const std::string& name) 276 { 277 BMCWEB_LOG_DEBUG("Get available Virtual Media resources."); 278 sdbusplus::message::object_path objPath( 279 "/xyz/openbmc_project/VirtualMedia"); 280 dbus::utility::getManagedObjects( 281 service, objPath, 282 [name, asyncResp{std::move(asyncResp)}]( 283 const boost::system::error_code& ec, 284 const dbus::utility::ManagedObjectType& subtree) { 285 if (ec) 286 { 287 BMCWEB_LOG_DEBUG("DBUS response error"); 288 return; 289 } 290 nlohmann::json& members = asyncResp->res.jsonValue["Members"]; 291 members = nlohmann::json::array(); 292 293 for (const auto& object : subtree) 294 { 295 nlohmann::json item; 296 std::string path = object.first.filename(); 297 if (path.empty()) 298 { 299 continue; 300 } 301 302 item["@odata.id"] = boost::urls::format( 303 "/redfish/v1/Managers/{}/VirtualMedia/{}", name, path); 304 members.emplace_back(std::move(item)); 305 } 306 asyncResp->res.jsonValue["Members@odata.count"] = members.size(); 307 }); 308 } 309 310 inline void 311 afterGetVmData(const std::string& name, const std::string& /*service*/, 312 const std::string& resName, 313 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 314 const std::pair<sdbusplus::message::object_path, 315 dbus::utility::DBusInterfacesMap>& item) 316 { 317 VmMode mode = parseObjectPathAndGetMode(item.first, resName); 318 if (mode == VmMode::Invalid) 319 { 320 return; 321 } 322 323 asyncResp->res.jsonValue = vmItemTemplate(name, resName); 324 325 // Check if dbus path is Legacy type 326 if (mode == VmMode::Legacy) 327 { 328 asyncResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"] 329 ["target"] = boost::urls::format( 330 "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.InsertMedia", 331 name, resName); 332 } 333 334 vmParseInterfaceObject(item.second, asyncResp); 335 336 asyncResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"] 337 ["target"] = boost::urls::format( 338 "/redfish/v1/Managers/{}/VirtualMedia/{}/Actions/VirtualMedia.EjectMedia", 339 name, resName); 340 } 341 342 /** 343 * @brief Fills data for specific resource 344 */ 345 inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 346 const std::string& service, const std::string& name, 347 const std::string& resName) 348 { 349 BMCWEB_LOG_DEBUG("Get Virtual Media resource data."); 350 351 findAndParseObject(service, resName, asyncResp, 352 std::bind_front(afterGetVmData, name)); 353 } 354 355 /** 356 * @brief Transfer protocols supported for InsertMedia action. 357 * 358 */ 359 enum class TransferProtocol 360 { 361 https, 362 smb, 363 invalid 364 }; 365 366 /** 367 * @brief Function extracts transfer protocol type from URI. 368 * 369 */ 370 inline std::optional<TransferProtocol> 371 getTransferProtocolFromUri(const boost::urls::url_view_base& imageUri) 372 { 373 std::string_view scheme = imageUri.scheme(); 374 if (scheme == "smb") 375 { 376 return TransferProtocol::smb; 377 } 378 if (scheme == "https") 379 { 380 return TransferProtocol::https; 381 } 382 if (!scheme.empty()) 383 { 384 return TransferProtocol::invalid; 385 } 386 387 return {}; 388 } 389 390 /** 391 * @brief Function convert transfer protocol from string param. 392 * 393 */ 394 inline std::optional<TransferProtocol> getTransferProtocolFromParam( 395 const std::optional<std::string>& transferProtocolType) 396 { 397 if (!transferProtocolType) 398 { 399 return {}; 400 } 401 402 if (*transferProtocolType == "CIFS") 403 { 404 return TransferProtocol::smb; 405 } 406 407 if (*transferProtocolType == "HTTPS") 408 { 409 return TransferProtocol::https; 410 } 411 412 return TransferProtocol::invalid; 413 } 414 415 /** 416 * @brief Function extends URI with transfer protocol type. 417 * 418 */ 419 inline std::string 420 getUriWithTransferProtocol(const std::string& imageUri, 421 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->fd(); 474 475 // Pass secret over pipe 476 secretPipe->asyncWrite( 477 std::move(userName), std::move(password), 478 [asyncResp, secretPipe](const boost::system::error_code& ec, 479 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 dbus::utility::DbusVariantType 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, secretPipe](const boost::system::error_code& ec, 496 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, "Image", actionParams.imageUrl, 716 "WriteProtected", actionParams.writeProtected, "UserName", 717 actionParams.userName, "Password", actionParams.password, 718 "Inserted", actionParams.inserted, "TransferMethod", 719 actionParams.transferMethod, "TransferProtocolType", 720 actionParams.transferProtocolType)) 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, resName); 754 755 return; 756 } 757 for (const auto& object : subtree) 758 { 759 VmMode mode = parseObjectPathAndGetMode(object.first, resName); 760 if (mode == VmMode::Legacy) 761 { 762 validateParams(asyncResp, service, resName, actionParams); 763 764 return; 765 } 766 } 767 BMCWEB_LOG_DEBUG("Parent item not found"); 768 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); 769 }); 770 }); 771 } 772 773 inline void handleManagersVirtualMediaActionEject( 774 crow::App& app, const crow::Request& req, 775 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 776 const std::string& managerName, const std::string& resName) 777 { 778 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 779 { 780 return; 781 } 782 783 constexpr std::string_view action = "VirtualMedia.EjectMedia"; 784 if (managerName != "bmc") 785 { 786 messages::resourceNotFound(asyncResp->res, action, resName); 787 788 return; 789 } 790 791 dbus::utility::getDbusObject( 792 "/xyz/openbmc_project/VirtualMedia", {}, 793 [asyncResp, action, 794 resName](const boost::system::error_code& ec2, 795 const dbus::utility::MapperGetObject& getObjectType) { 796 if (ec2) 797 { 798 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec2); 799 messages::internalError(asyncResp->res); 800 801 return; 802 } 803 std::string service = getObjectType.begin()->first; 804 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 805 806 sdbusplus::message::object_path path( 807 "/xyz/openbmc_project/VirtualMedia"); 808 dbus::utility::getManagedObjects( 809 service, path, 810 [resName, service, action, 811 asyncResp](const boost::system::error_code& ec, 812 const dbus::utility::ManagedObjectType& subtree) { 813 if (ec) 814 { 815 BMCWEB_LOG_ERROR("ObjectMapper : No Service found"); 816 messages::resourceNotFound(asyncResp->res, action, resName); 817 return; 818 } 819 820 for (const auto& object : subtree) 821 { 822 VmMode mode = parseObjectPathAndGetMode(object.first, resName); 823 if (mode != VmMode::Invalid) 824 { 825 doEjectAction(asyncResp, service, resName, 826 mode == VmMode::Legacy); 827 return; 828 } 829 } 830 BMCWEB_LOG_DEBUG("Parent item not found"); 831 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); 832 }); 833 }); 834 } 835 836 inline void handleManagersVirtualMediaCollectionGet( 837 crow::App& app, const crow::Request& req, 838 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 839 const std::string& name) 840 { 841 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 842 { 843 return; 844 } 845 if (name != "bmc") 846 { 847 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name); 848 849 return; 850 } 851 852 asyncResp->res.jsonValue["@odata.type"] = 853 "#VirtualMediaCollection.VirtualMediaCollection"; 854 asyncResp->res.jsonValue["Name"] = "Virtual Media Services"; 855 asyncResp->res.jsonValue["@odata.id"] = 856 boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name); 857 858 dbus::utility::getDbusObject( 859 "/xyz/openbmc_project/VirtualMedia", {}, 860 [asyncResp, name](const boost::system::error_code& ec, 861 const dbus::utility::MapperGetObject& getObjectType) { 862 if (ec) 863 { 864 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 865 messages::internalError(asyncResp->res); 866 867 return; 868 } 869 std::string service = getObjectType.begin()->first; 870 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 871 872 getVmResourceList(asyncResp, service, name); 873 }); 874 } 875 876 inline void 877 handleVirtualMediaGet(crow::App& app, const crow::Request& req, 878 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 879 const std::string& name, const std::string& resName) 880 { 881 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 882 { 883 return; 884 } 885 if (name != "bmc") 886 { 887 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); 888 889 return; 890 } 891 892 dbus::utility::getDbusObject( 893 "/xyz/openbmc_project/VirtualMedia", {}, 894 [asyncResp, name, 895 resName](const boost::system::error_code& ec, 896 const dbus::utility::MapperGetObject& getObjectType) { 897 if (ec) 898 { 899 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 900 messages::internalError(asyncResp->res); 901 902 return; 903 } 904 std::string service = getObjectType.begin()->first; 905 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 906 907 getVmData(asyncResp, service, name, resName); 908 }); 909 } 910 911 inline void requestNBDVirtualMediaRoutes(App& app) 912 { 913 BMCWEB_ROUTE( 914 app, 915 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia") 916 .privileges(redfish::privileges::postVirtualMedia) 917 .methods(boost::beast::http::verb::post)(std::bind_front( 918 handleManagersVirtualMediaActionInsertPost, std::ref(app))); 919 920 BMCWEB_ROUTE( 921 app, 922 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia") 923 .privileges(redfish::privileges::postVirtualMedia) 924 .methods(boost::beast::http::verb::post)(std::bind_front( 925 handleManagersVirtualMediaActionEject, std::ref(app))); 926 927 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/") 928 .privileges(redfish::privileges::getVirtualMediaCollection) 929 .methods(boost::beast::http::verb::get)(std::bind_front( 930 handleManagersVirtualMediaCollectionGet, std::ref(app))); 931 932 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/") 933 .privileges(redfish::privileges::getVirtualMedia) 934 .methods(boost::beast::http::verb::get)( 935 std::bind_front(handleVirtualMediaGet, std::ref(app))); 936 } 937 938 } // namespace redfish 939