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