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 dbus::utility::async_method_call( 505 asyncResp, 506 [asyncResp](const boost::system::error_code& ec, bool success) { 507 if (ec) 508 { 509 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); 510 messages::internalError(asyncResp->res); 511 return; 512 } 513 if (!success) 514 { 515 BMCWEB_LOG_ERROR("Service responded with error"); 516 messages::internalError(asyncResp->res); 517 } 518 }, 519 service, path.str, "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", 520 imageUrl, rw, unixFd); 521 } 522 523 /** 524 * @brief Function validate parameters of insert media request. 525 * 526 */ 527 inline void validateParams(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 528 const std::string& service, 529 const std::string& resName, 530 InsertMediaActionParams& actionParams) 531 { 532 BMCWEB_LOG_DEBUG("Validation started"); 533 // required param imageUrl must not be empty 534 if (!actionParams.imageUrl) 535 { 536 BMCWEB_LOG_ERROR("Request action parameter Image is empty."); 537 538 messages::propertyValueFormatError(asyncResp->res, "<empty>", "Image"); 539 540 return; 541 } 542 543 // optional param inserted must be true 544 if (actionParams.inserted && !*actionParams.inserted) 545 { 546 BMCWEB_LOG_ERROR( 547 "Request action optional parameter Inserted must be true."); 548 549 messages::actionParameterNotSupported(asyncResp->res, "Inserted", 550 "InsertMedia"); 551 552 return; 553 } 554 555 // optional param transferMethod must be stream 556 if (actionParams.transferMethod && 557 (*actionParams.transferMethod != "Stream")) 558 { 559 BMCWEB_LOG_ERROR("Request action optional parameter " 560 "TransferMethod must be Stream."); 561 562 messages::actionParameterNotSupported(asyncResp->res, "TransferMethod", 563 "InsertMedia"); 564 565 return; 566 } 567 boost::system::result<boost::urls::url_view> url = 568 boost::urls::parse_uri(*actionParams.imageUrl); 569 if (!url) 570 { 571 messages::actionParameterValueFormatError( 572 asyncResp->res, *actionParams.imageUrl, "Image", "InsertMedia"); 573 return; 574 } 575 std::optional<TransferProtocol> uriTransferProtocolType = 576 getTransferProtocolFromUri(*url); 577 578 std::optional<TransferProtocol> paramTransferProtocolType = 579 getTransferProtocolFromParam(actionParams.transferProtocolType); 580 581 // ImageUrl does not contain valid protocol type 582 if (uriTransferProtocolType && 583 *uriTransferProtocolType == TransferProtocol::invalid) 584 { 585 BMCWEB_LOG_ERROR("Request action parameter ImageUrl must " 586 "contain specified protocol type from list: " 587 "(smb, https)."); 588 589 messages::resourceAtUriInUnknownFormat(asyncResp->res, *url); 590 591 return; 592 } 593 594 // transferProtocolType should contain value from list 595 if (paramTransferProtocolType && 596 *paramTransferProtocolType == TransferProtocol::invalid) 597 { 598 BMCWEB_LOG_ERROR("Request action parameter TransferProtocolType " 599 "must be provided with value from list: " 600 "(CIFS, HTTPS)."); 601 602 messages::propertyValueNotInList( 603 asyncResp->res, actionParams.transferProtocolType.value_or(""), 604 "TransferProtocolType"); 605 return; 606 } 607 608 // valid transfer protocol not provided either with URI nor param 609 if (!uriTransferProtocolType && !paramTransferProtocolType) 610 { 611 BMCWEB_LOG_ERROR("Request action parameter ImageUrl must " 612 "contain specified protocol type or param " 613 "TransferProtocolType must be provided."); 614 615 messages::resourceAtUriInUnknownFormat(asyncResp->res, *url); 616 617 return; 618 } 619 620 // valid transfer protocol provided both with URI and param 621 if (paramTransferProtocolType && uriTransferProtocolType) 622 { 623 // check if protocol is the same for URI and param 624 if (*paramTransferProtocolType != *uriTransferProtocolType) 625 { 626 BMCWEB_LOG_ERROR("Request action parameter " 627 "TransferProtocolType must contain the " 628 "same protocol type as protocol type " 629 "provided with param imageUrl."); 630 631 messages::actionParameterValueTypeError( 632 asyncResp->res, actionParams.transferProtocolType.value_or(""), 633 "TransferProtocolType", "InsertMedia"); 634 635 return; 636 } 637 } 638 639 // validation passed, add protocol to URI if needed 640 if (!uriTransferProtocolType && paramTransferProtocolType) 641 { 642 actionParams.imageUrl = getUriWithTransferProtocol( 643 *actionParams.imageUrl, *paramTransferProtocolType); 644 } 645 646 if (!actionParams.userName) 647 { 648 actionParams.userName = ""; 649 } 650 651 if (!actionParams.password) 652 { 653 actionParams.password = ""; 654 } 655 656 doMountVmLegacy(asyncResp, service, resName, *actionParams.imageUrl, 657 !(actionParams.writeProtected.value_or(false)), 658 std::move(*actionParams.userName), 659 std::move(*actionParams.password)); 660 } 661 662 /** 663 * @brief Function transceives data with dbus directly. 664 * 665 * All BMC state properties will be retrieved before sending reset request. 666 */ 667 inline void doEjectAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 668 const std::string& service, const std::string& name, 669 bool legacy) 670 { 671 // Legacy mount requires parameter with image 672 if (legacy) 673 { 674 dbus::utility::async_method_call( 675 asyncResp, 676 [asyncResp](const boost::system::error_code& ec) { 677 if (ec) 678 { 679 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); 680 681 messages::internalError(asyncResp->res); 682 return; 683 } 684 }, 685 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, 686 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount"); 687 } 688 else // proxy 689 { 690 dbus::utility::async_method_call( 691 asyncResp, 692 [asyncResp](const boost::system::error_code& ec) { 693 if (ec) 694 { 695 BMCWEB_LOG_ERROR("Bad D-Bus request error: {}", ec); 696 697 messages::internalError(asyncResp->res); 698 return; 699 } 700 }, 701 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, 702 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); 703 } 704 } 705 706 inline void handleManagersVirtualMediaActionInsertPost( 707 crow::App& app, const crow::Request& req, 708 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 709 const std::string& name, const std::string& resName) 710 { 711 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 712 { 713 return; 714 } 715 716 constexpr std::string_view action = "VirtualMedia.InsertMedia"; 717 if (name != "bmc") 718 { 719 messages::resourceNotFound(asyncResp->res, action, resName); 720 721 return; 722 } 723 InsertMediaActionParams actionParams; 724 725 // Read obligatory parameters (url of image) 726 if (!json_util::readJsonAction( // 727 req, asyncResp->res, // 728 "Image", actionParams.imageUrl, // 729 "Inserted", actionParams.inserted, // 730 "Password", actionParams.password, // 731 "TransferMethod", actionParams.transferMethod, // 732 "TransferProtocolType", actionParams.transferProtocolType, // 733 "UserName", actionParams.userName, // 734 "WriteProtected", actionParams.writeProtected // 735 )) 736 { 737 return; 738 } 739 740 dbus::utility::getDbusObject( 741 "/xyz/openbmc_project/VirtualMedia", {}, 742 [asyncResp, action, actionParams, 743 resName](const boost::system::error_code& ec, 744 const dbus::utility::MapperGetObject& getObjectType) mutable { 745 if (ec) 746 { 747 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 748 messages::resourceNotFound(asyncResp->res, action, resName); 749 750 return; 751 } 752 753 std::string service = getObjectType.begin()->first; 754 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 755 756 sdbusplus::message::object_path path( 757 "/xyz/openbmc_project/VirtualMedia"); 758 dbus::utility::getManagedObjects( 759 service, path, 760 [service, resName, action, actionParams, asyncResp]( 761 const boost::system::error_code& ec2, 762 const dbus::utility::ManagedObjectType& subtree) mutable { 763 if (ec2) 764 { 765 // Not possible in proxy mode 766 BMCWEB_LOG_DEBUG("InsertMedia not " 767 "allowed in proxy mode"); 768 messages::resourceNotFound(asyncResp->res, action, 769 resName); 770 771 return; 772 } 773 for (const auto& object : subtree) 774 { 775 VmMode mode = 776 parseObjectPathAndGetMode(object.first, resName); 777 if (mode == VmMode::Legacy) 778 { 779 validateParams(asyncResp, service, resName, 780 actionParams); 781 782 return; 783 } 784 } 785 BMCWEB_LOG_DEBUG("Parent item not found"); 786 messages::resourceNotFound(asyncResp->res, "VirtualMedia", 787 resName); 788 }); 789 }); 790 } 791 792 inline void handleManagersVirtualMediaActionEject( 793 crow::App& app, const crow::Request& req, 794 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 795 const std::string& managerName, const std::string& resName) 796 { 797 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 798 { 799 return; 800 } 801 802 constexpr std::string_view action = "VirtualMedia.EjectMedia"; 803 if (managerName != "bmc") 804 { 805 messages::resourceNotFound(asyncResp->res, action, resName); 806 807 return; 808 } 809 810 dbus::utility::getDbusObject( 811 "/xyz/openbmc_project/VirtualMedia", {}, 812 [asyncResp, action, 813 resName](const boost::system::error_code& ec2, 814 const dbus::utility::MapperGetObject& getObjectType) { 815 if (ec2) 816 { 817 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", 818 ec2); 819 messages::internalError(asyncResp->res); 820 821 return; 822 } 823 std::string service = getObjectType.begin()->first; 824 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 825 826 sdbusplus::message::object_path path( 827 "/xyz/openbmc_project/VirtualMedia"); 828 dbus::utility::getManagedObjects( 829 service, path, 830 [resName, service, action, 831 asyncResp](const boost::system::error_code& ec, 832 const dbus::utility::ManagedObjectType& subtree) { 833 if (ec) 834 { 835 BMCWEB_LOG_ERROR("ObjectMapper : No Service found"); 836 messages::resourceNotFound(asyncResp->res, action, 837 resName); 838 return; 839 } 840 841 for (const auto& object : subtree) 842 { 843 VmMode mode = 844 parseObjectPathAndGetMode(object.first, resName); 845 if (mode != VmMode::Invalid) 846 { 847 doEjectAction(asyncResp, service, resName, 848 mode == VmMode::Legacy); 849 return; 850 } 851 } 852 BMCWEB_LOG_DEBUG("Parent item not found"); 853 messages::resourceNotFound(asyncResp->res, "VirtualMedia", 854 resName); 855 }); 856 }); 857 } 858 859 inline void handleManagersVirtualMediaCollectionGet( 860 crow::App& app, const crow::Request& req, 861 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 862 const std::string& name) 863 { 864 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 865 { 866 return; 867 } 868 if (name != "bmc") 869 { 870 messages::resourceNotFound(asyncResp->res, "VirtualMedia", name); 871 872 return; 873 } 874 875 asyncResp->res.jsonValue["@odata.type"] = 876 "#VirtualMediaCollection.VirtualMediaCollection"; 877 asyncResp->res.jsonValue["Name"] = "Virtual Media Services"; 878 asyncResp->res.jsonValue["@odata.id"] = 879 boost::urls::format("/redfish/v1/Managers/{}/VirtualMedia", name); 880 881 dbus::utility::getDbusObject( 882 "/xyz/openbmc_project/VirtualMedia", {}, 883 [asyncResp, name](const boost::system::error_code& ec, 884 const dbus::utility::MapperGetObject& getObjectType) { 885 if (ec) 886 { 887 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 888 messages::internalError(asyncResp->res); 889 890 return; 891 } 892 std::string service = getObjectType.begin()->first; 893 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 894 895 getVmResourceList(asyncResp, service, name); 896 }); 897 } 898 899 inline void handleVirtualMediaGet( 900 crow::App& app, const crow::Request& req, 901 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 902 const std::string& name, const std::string& resName) 903 { 904 if (!redfish::setUpRedfishRoute(app, req, asyncResp)) 905 { 906 return; 907 } 908 if (name != "bmc") 909 { 910 messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); 911 912 return; 913 } 914 915 dbus::utility::getDbusObject( 916 "/xyz/openbmc_project/VirtualMedia", {}, 917 [asyncResp, name, 918 resName](const boost::system::error_code& ec, 919 const dbus::utility::MapperGetObject& getObjectType) { 920 if (ec) 921 { 922 BMCWEB_LOG_ERROR("ObjectMapper::GetObject call failed: {}", ec); 923 messages::internalError(asyncResp->res); 924 925 return; 926 } 927 std::string service = getObjectType.begin()->first; 928 BMCWEB_LOG_DEBUG("GetObjectType: {}", service); 929 930 getVmData(asyncResp, service, name, resName); 931 }); 932 } 933 934 inline void requestNBDVirtualMediaRoutes(App& app) 935 { 936 BMCWEB_ROUTE( 937 app, 938 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia") 939 .privileges(redfish::privileges::postVirtualMedia) 940 .methods(boost::beast::http::verb::post)(std::bind_front( 941 handleManagersVirtualMediaActionInsertPost, std::ref(app))); 942 943 BMCWEB_ROUTE( 944 app, 945 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia") 946 .privileges(redfish::privileges::postVirtualMedia) 947 .methods(boost::beast::http::verb::post)(std::bind_front( 948 handleManagersVirtualMediaActionEject, std::ref(app))); 949 950 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/") 951 .privileges(redfish::privileges::getVirtualMediaCollection) 952 .methods(boost::beast::http::verb::get)(std::bind_front( 953 handleManagersVirtualMediaCollectionGet, std::ref(app))); 954 955 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/") 956 .privileges(redfish::privileges::getVirtualMedia) 957 .methods(boost::beast::http::verb::get)( 958 std::bind_front(handleVirtualMediaGet, std::ref(app))); 959 } 960 961 } // namespace redfish 962