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