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