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