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 <app.hpp> 19 #include <boost/container/flat_map.hpp> 20 #include <boost/process/async_pipe.hpp> 21 #include <boost/type_traits/has_dereference.hpp> 22 #include <utils/json_utils.hpp> 23 // for GetObjectType and ManagedObjectType 24 25 #include <account_service.hpp> 26 #include <boost/url/url_view.hpp> 27 #include <registries/privilege_registry.hpp> 28 29 namespace redfish 30 { 31 /** 32 * @brief Function extracts transfer protocol name from URI. 33 */ 34 inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri) 35 { 36 boost::urls::error_code ec; 37 boost::urls::url_view url = 38 boost::urls::parse_uri(boost::string_view(imageUri), ec); 39 if (ec) 40 { 41 return "None"; 42 } 43 boost::string_view scheme = url.scheme(); 44 if (scheme == "smb") 45 { 46 return "CIFS"; 47 } 48 if (scheme == "https") 49 { 50 return "HTTPS"; 51 } 52 53 return "None"; 54 } 55 56 /** 57 * @brief Read all known properties from VM object interfaces 58 */ 59 inline void 60 vmParseInterfaceObject(const dbus::utility::DBusInteracesMap& interface, 61 const std::shared_ptr<bmcweb::AsyncResp>& aResp) 62 { 63 for (const auto& [interface, values] : interface) 64 { 65 if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint") 66 { 67 for (const auto& [property, value] : values) 68 { 69 if (property == "EndpointId") 70 { 71 const std::string* endpointIdValue = 72 std::get_if<std::string>(&value); 73 if (endpointIdValue == nullptr) 74 { 75 continue; 76 } 77 if (!endpointIdValue->empty()) 78 { 79 // Proxy mode 80 aResp->res 81 .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] = 82 *endpointIdValue; 83 aResp->res.jsonValue["TransferProtocolType"] = "OEM"; 84 } 85 } 86 if (property == "ImageURL") 87 { 88 const std::string* imageUrlValue = 89 std::get_if<std::string>(&value); 90 if (imageUrlValue && !imageUrlValue->empty()) 91 { 92 std::filesystem::path filePath = *imageUrlValue; 93 if (!filePath.has_filename()) 94 { 95 // this will handle https share, which not 96 // necessarily has to have filename given. 97 aResp->res.jsonValue["ImageName"] = ""; 98 } 99 else 100 { 101 aResp->res.jsonValue["ImageName"] = 102 filePath.filename(); 103 } 104 105 aResp->res.jsonValue["Image"] = *imageUrlValue; 106 aResp->res.jsonValue["TransferProtocolType"] = 107 getTransferProtocolTypeFromUri(*imageUrlValue); 108 109 aResp->res.jsonValue["ConnectedVia"] = "URI"; 110 } 111 } 112 if (property == "WriteProtected") 113 { 114 const bool* writeProtectedValue = std::get_if<bool>(&value); 115 if (writeProtectedValue) 116 { 117 aResp->res.jsonValue["WriteProtected"] = 118 *writeProtectedValue; 119 } 120 } 121 } 122 } 123 if (interface == "xyz.openbmc_project.VirtualMedia.Process") 124 { 125 for (const auto& [property, value] : values) 126 { 127 if (property == "Active") 128 { 129 const bool* activeValue = std::get_if<bool>(&value); 130 if (!activeValue) 131 { 132 BMCWEB_LOG_DEBUG << "Value Active not found"; 133 return; 134 } 135 aResp->res.jsonValue["Inserted"] = *activeValue; 136 137 if (*activeValue == true) 138 { 139 aResp->res.jsonValue["ConnectedVia"] = "Applet"; 140 } 141 } 142 } 143 } 144 } 145 } 146 147 /** 148 * @brief Fill template for Virtual Media Item. 149 */ 150 inline nlohmann::json vmItemTemplate(const std::string& name, 151 const std::string& resName) 152 { 153 nlohmann::json item; 154 155 std::string id = "/redfish/v1/Managers/"; 156 id += name; 157 id += "/VirtualMedia/"; 158 id += resName; 159 item["@odata.id"] = std::move(id); 160 161 item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia"; 162 item["Name"] = "Virtual Removable Media"; 163 item["Id"] = resName; 164 item["WriteProtected"] = true; 165 item["MediaTypes"] = {"CD", "USBStick"}; 166 item["TransferMethod"] = "Stream"; 167 item["Oem"]["OpenBMC"]["@odata.type"] = 168 "#OemVirtualMedia.v1_0_0.VirtualMedia"; 169 170 return item; 171 } 172 173 /** 174 * @brief Fills collection data 175 */ 176 inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> aResp, 177 const std::string& service, 178 const std::string& name) 179 { 180 BMCWEB_LOG_DEBUG << "Get available Virtual Media resources."; 181 crow::connections::systemBus->async_method_call( 182 [name, 183 aResp{std::move(aResp)}](const boost::system::error_code ec, 184 dbus::utility::ManagedObjectType& subtree) { 185 if (ec) 186 { 187 BMCWEB_LOG_DEBUG << "DBUS response error"; 188 return; 189 } 190 nlohmann::json& members = aResp->res.jsonValue["Members"]; 191 members = nlohmann::json::array(); 192 193 for (const auto& object : subtree) 194 { 195 nlohmann::json item; 196 std::string path = object.first.filename(); 197 if (path.empty()) 198 { 199 continue; 200 } 201 202 std::string id = "/redfish/v1/Managers/"; 203 id += name; 204 id += "/VirtualMedia/"; 205 id += path; 206 207 item["@odata.id"] = std::move(id); 208 members.emplace_back(std::move(item)); 209 } 210 aResp->res.jsonValue["Members@odata.count"] = members.size(); 211 }, 212 service, "/xyz/openbmc_project/VirtualMedia", 213 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 214 } 215 216 /** 217 * @brief Fills data for specific resource 218 */ 219 inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, 220 const std::string& service, const std::string& name, 221 const std::string& resName) 222 { 223 BMCWEB_LOG_DEBUG << "Get Virtual Media resource data."; 224 225 crow::connections::systemBus->async_method_call( 226 [resName, name, aResp](const boost::system::error_code ec, 227 dbus::utility::ManagedObjectType& subtree) { 228 if (ec) 229 { 230 BMCWEB_LOG_DEBUG << "DBUS response error"; 231 232 return; 233 } 234 235 for (auto& item : subtree) 236 { 237 std::string thispath = item.first.filename(); 238 if (thispath.empty()) 239 { 240 continue; 241 } 242 243 if (thispath != resName) 244 { 245 continue; 246 } 247 248 // "Legacy"/"Proxy" 249 auto mode = item.first.parent_path(); 250 // "VirtualMedia" 251 auto type = mode.parent_path(); 252 if (mode.filename().empty() || type.filename().empty()) 253 { 254 continue; 255 } 256 257 if (type.filename() != "VirtualMedia") 258 { 259 continue; 260 } 261 262 aResp->res.jsonValue = vmItemTemplate(name, resName); 263 std::string actionsId = "/redfish/v1/Managers/"; 264 actionsId += name; 265 actionsId += "/VirtualMedia/"; 266 actionsId += resName; 267 actionsId += "/Actions"; 268 269 // Check if dbus path is Legacy type 270 if (mode.filename() == "Legacy") 271 { 272 aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"] 273 ["target"] = 274 actionsId + "/VirtualMedia.InsertMedia"; 275 } 276 277 vmParseInterfaceObject(item.second, aResp); 278 279 aResp->res.jsonValue["Actions"]["#VirtualMedia.EjectMedia"] 280 ["target"] = 281 actionsId + "/VirtualMedia.EjectMedia"; 282 283 return; 284 } 285 286 messages::resourceNotFound( 287 aResp->res, "#VirtualMedia.v1_3_0.VirtualMedia", resName); 288 }, 289 service, "/xyz/openbmc_project/VirtualMedia", 290 "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); 291 } 292 293 /** 294 * @brief Transfer protocols supported for InsertMedia action. 295 * 296 */ 297 enum class TransferProtocol 298 { 299 https, 300 smb, 301 invalid 302 }; 303 304 /** 305 * @brief Function extracts transfer protocol type from URI. 306 * 307 */ 308 inline std::optional<TransferProtocol> 309 getTransferProtocolFromUri(const std::string& imageUri) 310 { 311 boost::urls::error_code ec; 312 boost::urls::url_view url = 313 boost::urls::parse_uri(boost::string_view(imageUri), ec); 314 if (ec) 315 { 316 return {}; 317 } 318 319 boost::string_view scheme = url.scheme(); 320 if (scheme == "smb") 321 { 322 return TransferProtocol::smb; 323 } 324 if (scheme == "https") 325 { 326 return TransferProtocol::https; 327 } 328 if (!scheme.empty()) 329 { 330 return TransferProtocol::invalid; 331 } 332 333 return {}; 334 } 335 336 /** 337 * @brief Function convert transfer protocol from string param. 338 * 339 */ 340 inline std::optional<TransferProtocol> getTransferProtocolFromParam( 341 const std::optional<std::string>& transferProtocolType) 342 { 343 if (transferProtocolType == std::nullopt) 344 { 345 return {}; 346 } 347 348 if (*transferProtocolType == "CIFS") 349 { 350 return TransferProtocol::smb; 351 } 352 353 if (*transferProtocolType == "HTTPS") 354 { 355 return TransferProtocol::https; 356 } 357 358 return TransferProtocol::invalid; 359 } 360 361 /** 362 * @brief Function extends URI with transfer protocol type. 363 * 364 */ 365 inline std::string 366 getUriWithTransferProtocol(const std::string& imageUri, 367 const TransferProtocol& transferProtocol) 368 { 369 if (transferProtocol == TransferProtocol::smb) 370 { 371 return "smb://" + imageUri; 372 } 373 374 if (transferProtocol == TransferProtocol::https) 375 { 376 return "https://" + imageUri; 377 } 378 379 return imageUri; 380 } 381 382 /** 383 * @brief Function validate parameters of insert media request. 384 * 385 */ 386 inline bool 387 validateParams(crow::Response& res, std::string& imageUrl, 388 const std::optional<bool>& inserted, 389 const std::optional<std::string>& transferMethod, 390 const std::optional<std::string>& transferProtocolType) 391 { 392 BMCWEB_LOG_DEBUG << "Validation started"; 393 // required param imageUrl must not be empty 394 if (imageUrl.empty()) 395 { 396 BMCWEB_LOG_ERROR << "Request action parameter Image is empty."; 397 398 messages::propertyValueFormatError(res, "<empty>", "Image"); 399 400 return false; 401 } 402 403 // optional param inserted must be true 404 if ((inserted != std::nullopt) && (*inserted != true)) 405 { 406 BMCWEB_LOG_ERROR 407 << "Request action optional parameter Inserted must be true."; 408 409 messages::actionParameterNotSupported(res, "Inserted", "InsertMedia"); 410 411 return false; 412 } 413 414 // optional param transferMethod must be stream 415 if ((transferMethod != std::nullopt) && (*transferMethod != "Stream")) 416 { 417 BMCWEB_LOG_ERROR << "Request action optional parameter " 418 "TransferMethod must be Stream."; 419 420 messages::actionParameterNotSupported(res, "TransferMethod", 421 "InsertMedia"); 422 423 return false; 424 } 425 426 std::optional<TransferProtocol> uriTransferProtocolType = 427 getTransferProtocolFromUri(imageUrl); 428 429 std::optional<TransferProtocol> paramTransferProtocolType = 430 getTransferProtocolFromParam(transferProtocolType); 431 432 // ImageUrl does not contain valid protocol type 433 if (*uriTransferProtocolType == TransferProtocol::invalid) 434 { 435 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must " 436 "contain specified protocol type from list: " 437 "(smb, https)."; 438 439 messages::resourceAtUriInUnknownFormat(res, imageUrl); 440 441 return false; 442 } 443 444 // transferProtocolType should contain value from list 445 if (*paramTransferProtocolType == TransferProtocol::invalid) 446 { 447 BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType " 448 "must be provided with value from list: " 449 "(CIFS, HTTPS)."; 450 451 messages::propertyValueNotInList(res, *transferProtocolType, 452 "TransferProtocolType"); 453 return false; 454 } 455 456 // valid transfer protocol not provided either with URI nor param 457 if ((uriTransferProtocolType == std::nullopt) && 458 (paramTransferProtocolType == std::nullopt)) 459 { 460 BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must " 461 "contain specified protocol type or param " 462 "TransferProtocolType must be provided."; 463 464 messages::resourceAtUriInUnknownFormat(res, imageUrl); 465 466 return false; 467 } 468 469 // valid transfer protocol provided both with URI and param 470 if ((paramTransferProtocolType != std::nullopt) && 471 (uriTransferProtocolType != std::nullopt)) 472 { 473 // check if protocol is the same for URI and param 474 if (*paramTransferProtocolType != *uriTransferProtocolType) 475 { 476 BMCWEB_LOG_ERROR << "Request action parameter " 477 "TransferProtocolType must contain the " 478 "same protocol type as protocol type " 479 "provided with param imageUrl."; 480 481 messages::actionParameterValueTypeError(res, *transferProtocolType, 482 "TransferProtocolType", 483 "InsertMedia"); 484 485 return false; 486 } 487 } 488 489 // validation passed 490 // add protocol to URI if needed 491 if (uriTransferProtocolType == std::nullopt) 492 { 493 imageUrl = 494 getUriWithTransferProtocol(imageUrl, *paramTransferProtocolType); 495 } 496 497 return true; 498 } 499 500 template <typename T> 501 static void secureCleanup(T& value) 502 { 503 auto raw = const_cast<typename T::value_type*>(value.data()); 504 explicit_bzero(raw, value.size() * sizeof(*raw)); 505 } 506 507 class Credentials 508 { 509 public: 510 Credentials(std::string&& user, std::string&& password) : 511 userBuf(std::move(user)), passBuf(std::move(password)) 512 {} 513 514 ~Credentials() 515 { 516 secureCleanup(userBuf); 517 secureCleanup(passBuf); 518 } 519 520 const std::string& user() 521 { 522 return userBuf; 523 } 524 525 const std::string& password() 526 { 527 return passBuf; 528 } 529 530 Credentials() = delete; 531 Credentials(const Credentials&) = delete; 532 Credentials& operator=(const Credentials&) = delete; 533 534 private: 535 std::string userBuf; 536 std::string passBuf; 537 }; 538 539 class CredentialsProvider 540 { 541 public: 542 template <typename T> 543 struct Deleter 544 { 545 void operator()(T* buff) const 546 { 547 if (buff) 548 { 549 secureCleanup(*buff); 550 delete buff; 551 } 552 } 553 }; 554 555 using Buffer = std::vector<char>; 556 using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>; 557 // Using explicit definition instead of std::function to avoid implicit 558 // conversions eg. stack copy instead of reference 559 using FormatterFunc = void(const std::string& username, 560 const std::string& password, Buffer& dest); 561 562 CredentialsProvider(std::string&& user, std::string&& password) : 563 credentials(std::move(user), std::move(password)) 564 {} 565 566 const std::string& user() 567 { 568 return credentials.user(); 569 } 570 571 const std::string& password() 572 { 573 return credentials.password(); 574 } 575 576 SecureBuffer pack(FormatterFunc formatter) 577 { 578 SecureBuffer packed{new Buffer{}}; 579 if (formatter) 580 { 581 formatter(credentials.user(), credentials.password(), *packed); 582 } 583 584 return packed; 585 } 586 587 private: 588 Credentials credentials; 589 }; 590 591 // Wrapper for boost::async_pipe ensuring proper pipe cleanup 592 template <typename Buffer> 593 class Pipe 594 { 595 public: 596 using unix_fd = sdbusplus::message::unix_fd; 597 598 Pipe(boost::asio::io_context& io, Buffer&& buffer) : 599 impl(io), buffer{std::move(buffer)} 600 {} 601 602 ~Pipe() 603 { 604 // Named pipe needs to be explicitly removed 605 impl.close(); 606 } 607 608 unix_fd fd() 609 { 610 return unix_fd{impl.native_source()}; 611 } 612 613 template <typename WriteHandler> 614 void asyncWrite(WriteHandler&& handler) 615 { 616 impl.async_write_some(data(), std::forward<WriteHandler>(handler)); 617 } 618 619 private: 620 // Specialization for pointer types 621 template <typename B = Buffer> 622 typename std::enable_if<boost::has_dereference<B>::value, 623 boost::asio::const_buffer>::type 624 data() 625 { 626 return boost::asio::buffer(*buffer); 627 } 628 629 template <typename B = Buffer> 630 typename std::enable_if<!boost::has_dereference<B>::value, 631 boost::asio::const_buffer>::type 632 data() 633 { 634 return boost::asio::buffer(buffer); 635 } 636 637 const std::string name; 638 boost::process::async_pipe impl; 639 Buffer buffer; 640 }; 641 642 /** 643 * @brief Function transceives data with dbus directly. 644 * 645 * All BMC state properties will be retrieved before sending reset request. 646 */ 647 inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 648 const std::string& service, const std::string& name, 649 const std::string& imageUrl, const bool rw, 650 std::string&& userName, std::string&& password) 651 { 652 using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>; 653 constexpr const size_t secretLimit = 1024; 654 655 std::shared_ptr<SecurePipe> secretPipe; 656 dbus::utility::DbusVariantType unixFd = -1; 657 658 if (!userName.empty() || !password.empty()) 659 { 660 // Encapsulate in safe buffer 661 CredentialsProvider credentials(std::move(userName), 662 std::move(password)); 663 664 // Payload must contain data + NULL delimiters 665 if (credentials.user().size() + credentials.password().size() + 2 > 666 secretLimit) 667 { 668 BMCWEB_LOG_ERROR << "Credentials too long to handle"; 669 messages::unrecognizedRequestBody(asyncResp->res); 670 return; 671 } 672 673 // Pack secret 674 auto secret = credentials.pack( 675 [](const auto& user, const auto& pass, auto& buff) { 676 std::copy(user.begin(), user.end(), std::back_inserter(buff)); 677 buff.push_back('\0'); 678 std::copy(pass.begin(), pass.end(), std::back_inserter(buff)); 679 buff.push_back('\0'); 680 }); 681 682 // Open pipe 683 secretPipe = std::make_shared<SecurePipe>( 684 crow::connections::systemBus->get_io_context(), std::move(secret)); 685 unixFd = secretPipe->fd(); 686 687 // Pass secret over pipe 688 secretPipe->asyncWrite( 689 [asyncResp](const boost::system::error_code& ec, std::size_t) { 690 if (ec) 691 { 692 BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec; 693 messages::internalError(asyncResp->res); 694 } 695 }); 696 } 697 698 crow::connections::systemBus->async_method_call( 699 [asyncResp, secretPipe](const boost::system::error_code ec, 700 bool success) { 701 if (ec) 702 { 703 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; 704 messages::internalError(asyncResp->res); 705 } 706 else if (!success) 707 { 708 BMCWEB_LOG_ERROR << "Service responded with error"; 709 messages::generalError(asyncResp->res); 710 } 711 }, 712 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, 713 "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw, 714 unixFd); 715 } 716 717 /** 718 * @brief Function transceives data with dbus directly. 719 * 720 * All BMC state properties will be retrieved before sending reset request. 721 */ 722 inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 723 const std::string& service, const std::string& name, 724 bool legacy) 725 { 726 727 // Legacy mount requires parameter with image 728 if (legacy) 729 { 730 crow::connections::systemBus->async_method_call( 731 [asyncResp](const boost::system::error_code ec) { 732 if (ec) 733 { 734 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; 735 736 messages::internalError(asyncResp->res); 737 return; 738 } 739 }, 740 service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, 741 "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount"); 742 } 743 else // proxy 744 { 745 crow::connections::systemBus->async_method_call( 746 [asyncResp](const boost::system::error_code ec) { 747 if (ec) 748 { 749 BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; 750 751 messages::internalError(asyncResp->res); 752 return; 753 } 754 }, 755 service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, 756 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); 757 } 758 } 759 760 struct InsertMediaActionParams 761 { 762 std::string imageUrl; 763 std::optional<std::string> userName; 764 std::optional<std::string> password; 765 std::optional<std::string> transferMethod; 766 std::optional<std::string> transferProtocolType; 767 std::optional<bool> writeProtected = true; 768 std::optional<bool> inserted; 769 }; 770 771 inline void requestNBDVirtualMediaRoutes(App& app) 772 { 773 BMCWEB_ROUTE( 774 app, 775 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia") 776 .privileges(redfish::privileges::postVirtualMedia) 777 .methods(boost::beast::http::verb::post)( 778 [](const crow::Request& req, 779 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 780 const std::string& name, const std::string& resName) { 781 if (name != "bmc") 782 { 783 messages::resourceNotFound(asyncResp->res, 784 "VirtualMedia.Insert", resName); 785 786 return; 787 } 788 InsertMediaActionParams actionParams; 789 790 // Read obligatory parameters (url of 791 // image) 792 if (!json_util::readJson( 793 req, asyncResp->res, "Image", actionParams.imageUrl, 794 "WriteProtected", actionParams.writeProtected, 795 "UserName", actionParams.userName, "Password", 796 actionParams.password, "Inserted", 797 actionParams.inserted, "TransferMethod", 798 actionParams.transferMethod, "TransferProtocolType", 799 actionParams.transferProtocolType)) 800 { 801 BMCWEB_LOG_DEBUG << "Image is not provided"; 802 return; 803 } 804 805 bool paramsValid = validateParams( 806 asyncResp->res, actionParams.imageUrl, 807 actionParams.inserted, actionParams.transferMethod, 808 actionParams.transferProtocolType); 809 810 if (paramsValid == false) 811 { 812 return; 813 } 814 815 crow::connections::systemBus->async_method_call( 816 [asyncResp, actionParams, 817 resName](const boost::system::error_code ec, 818 const GetObjectType& getObjectType) mutable { 819 if (ec) 820 { 821 BMCWEB_LOG_ERROR 822 << "ObjectMapper::GetObject call failed: " 823 << ec; 824 messages::internalError(asyncResp->res); 825 826 return; 827 } 828 std::string service = getObjectType.begin()->first; 829 BMCWEB_LOG_DEBUG << "GetObjectType: " << service; 830 831 crow::connections::systemBus->async_method_call( 832 [service, resName, actionParams, 833 asyncResp](const boost::system::error_code ec, 834 dbus::utility::ManagedObjectType& 835 subtree) mutable { 836 if (ec) 837 { 838 BMCWEB_LOG_DEBUG << "DBUS response error"; 839 840 return; 841 } 842 843 for (const auto& object : subtree) 844 { 845 const std::string& path = 846 static_cast<const std::string&>( 847 object.first); 848 849 std::size_t lastIndex = path.rfind('/'); 850 if (lastIndex == std::string::npos) 851 { 852 continue; 853 } 854 855 lastIndex += 1; 856 857 if (path.substr(lastIndex) == resName) 858 { 859 lastIndex = path.rfind("Proxy"); 860 if (lastIndex != std::string::npos) 861 { 862 // Not possible in proxy mode 863 BMCWEB_LOG_DEBUG 864 << "InsertMedia not " 865 "allowed in proxy mode"; 866 messages::resourceNotFound( 867 asyncResp->res, 868 "VirtualMedia.InsertMedia", 869 resName); 870 871 return; 872 } 873 874 lastIndex = path.rfind("Legacy"); 875 if (lastIndex == std::string::npos) 876 { 877 continue; 878 } 879 880 // manager is irrelevant for 881 // VirtualMedia dbus calls 882 doMountVmLegacy( 883 asyncResp, service, resName, 884 actionParams.imageUrl, 885 !(*actionParams.writeProtected), 886 std::move(*actionParams.userName), 887 std::move(*actionParams.password)); 888 889 return; 890 } 891 } 892 BMCWEB_LOG_DEBUG << "Parent item not found"; 893 messages::resourceNotFound( 894 asyncResp->res, "VirtualMedia", resName); 895 }, 896 service, "/xyz/openbmc_project/VirtualMedia", 897 "org.freedesktop.DBus.ObjectManager", 898 "GetManagedObjects"); 899 }, 900 "xyz.openbmc_project.ObjectMapper", 901 "/xyz/openbmc_project/object_mapper", 902 "xyz.openbmc_project.ObjectMapper", "GetObject", 903 "/xyz/openbmc_project/VirtualMedia", 904 std::array<const char*, 0>()); 905 }); 906 907 BMCWEB_ROUTE( 908 app, 909 "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia") 910 .privileges(redfish::privileges::postVirtualMedia) 911 .methods(boost::beast::http::verb::post)( 912 [](const crow::Request&, 913 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 914 const std::string& name, const std::string& resName) { 915 if (name != "bmc") 916 { 917 messages::resourceNotFound(asyncResp->res, 918 "VirtualMedia.Eject", resName); 919 920 return; 921 } 922 923 crow::connections::systemBus->async_method_call( 924 [asyncResp, resName](const boost::system::error_code ec, 925 const GetObjectType& getObjectType) { 926 if (ec) 927 { 928 BMCWEB_LOG_ERROR 929 << "ObjectMapper::GetObject call failed: " 930 << ec; 931 messages::internalError(asyncResp->res); 932 933 return; 934 } 935 std::string service = getObjectType.begin()->first; 936 BMCWEB_LOG_DEBUG << "GetObjectType: " << service; 937 938 crow::connections::systemBus->async_method_call( 939 [resName, service, asyncResp{asyncResp}]( 940 const boost::system::error_code ec, 941 dbus::utility::ManagedObjectType& subtree) { 942 if (ec) 943 { 944 BMCWEB_LOG_DEBUG << "DBUS response error"; 945 946 return; 947 } 948 949 for (const auto& object : subtree) 950 { 951 const std::string& path = 952 static_cast<const std::string&>( 953 object.first); 954 955 std::size_t lastIndex = path.rfind('/'); 956 if (lastIndex == std::string::npos) 957 { 958 continue; 959 } 960 961 lastIndex += 1; 962 963 if (path.substr(lastIndex) == resName) 964 { 965 lastIndex = path.rfind("Proxy"); 966 if (lastIndex != std::string::npos) 967 { 968 // Proxy mode 969 doVmAction(asyncResp, service, 970 resName, false); 971 } 972 973 lastIndex = path.rfind("Legacy"); 974 if (lastIndex != std::string::npos) 975 { 976 // Legacy mode 977 doVmAction(asyncResp, service, 978 resName, true); 979 } 980 981 return; 982 } 983 } 984 BMCWEB_LOG_DEBUG << "Parent item not found"; 985 messages::resourceNotFound( 986 asyncResp->res, "VirtualMedia", resName); 987 }, 988 service, "/xyz/openbmc_project/VirtualMedia", 989 "org.freedesktop.DBus.ObjectManager", 990 "GetManagedObjects"); 991 }, 992 "xyz.openbmc_project.ObjectMapper", 993 "/xyz/openbmc_project/object_mapper", 994 "xyz.openbmc_project.ObjectMapper", "GetObject", 995 "/xyz/openbmc_project/VirtualMedia", 996 std::array<const char*, 0>()); 997 }); 998 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/") 999 .privileges(redfish::privileges::getVirtualMediaCollection) 1000 .methods(boost::beast::http::verb::get)( 1001 [](const crow::Request& /* req */, 1002 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1003 const std::string& name) { 1004 if (name != "bmc") 1005 { 1006 messages::resourceNotFound(asyncResp->res, "VirtualMedia", 1007 name); 1008 1009 return; 1010 } 1011 1012 asyncResp->res.jsonValue["@odata.type"] = 1013 "#VirtualMediaCollection.VirtualMediaCollection"; 1014 asyncResp->res.jsonValue["Name"] = "Virtual Media Services"; 1015 asyncResp->res.jsonValue["@odata.id"] = 1016 "/redfish/v1/Managers/" + name + "/VirtualMedia"; 1017 1018 crow::connections::systemBus->async_method_call( 1019 [asyncResp, name](const boost::system::error_code ec, 1020 const GetObjectType& getObjectType) { 1021 if (ec) 1022 { 1023 BMCWEB_LOG_ERROR 1024 << "ObjectMapper::GetObject call failed: " 1025 << ec; 1026 messages::internalError(asyncResp->res); 1027 1028 return; 1029 } 1030 std::string service = getObjectType.begin()->first; 1031 BMCWEB_LOG_DEBUG << "GetObjectType: " << service; 1032 1033 getVmResourceList(asyncResp, service, name); 1034 }, 1035 "xyz.openbmc_project.ObjectMapper", 1036 "/xyz/openbmc_project/object_mapper", 1037 "xyz.openbmc_project.ObjectMapper", "GetObject", 1038 "/xyz/openbmc_project/VirtualMedia", 1039 std::array<const char*, 0>()); 1040 }); 1041 1042 BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/") 1043 .privileges(redfish::privileges::getVirtualMedia) 1044 .methods(boost::beast::http::verb::get)( 1045 [](const crow::Request& /* req */, 1046 const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, 1047 const std::string& name, const std::string& resName) { 1048 if (name != "bmc") 1049 { 1050 messages::resourceNotFound(asyncResp->res, "VirtualMedia", 1051 resName); 1052 1053 return; 1054 } 1055 1056 crow::connections::systemBus->async_method_call( 1057 [asyncResp, name, 1058 resName](const boost::system::error_code ec, 1059 const GetObjectType& getObjectType) { 1060 if (ec) 1061 { 1062 BMCWEB_LOG_ERROR 1063 << "ObjectMapper::GetObject call failed: " 1064 << ec; 1065 messages::internalError(asyncResp->res); 1066 1067 return; 1068 } 1069 std::string service = getObjectType.begin()->first; 1070 BMCWEB_LOG_DEBUG << "GetObjectType: " << service; 1071 1072 getVmData(asyncResp, service, name, resName); 1073 }, 1074 "xyz.openbmc_project.ObjectMapper", 1075 "/xyz/openbmc_project/object_mapper", 1076 "xyz.openbmc_project.ObjectMapper", "GetObject", 1077 "/xyz/openbmc_project/VirtualMedia", 1078 std::array<const char*, 0>()); 1079 }); 1080 } 1081 1082 } // namespace redfish 1083