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