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