| /* |
| // Copyright (c) 2018 Intel Corporation |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| */ |
| #pragma once |
| |
| #include <account_service.hpp> |
| #include <app.hpp> |
| #include <boost/process/async_pipe.hpp> |
| #include <boost/type_traits/has_dereference.hpp> |
| #include <boost/url/url_view.hpp> |
| #include <query.hpp> |
| #include <registries/privilege_registry.hpp> |
| #include <utils/json_utils.hpp> |
| |
| namespace redfish |
| { |
| /** |
| * @brief Function extracts transfer protocol name from URI. |
| */ |
| inline std::string getTransferProtocolTypeFromUri(const std::string& imageUri) |
| { |
| boost::urls::result<boost::urls::url_view> url = |
| boost::urls::parse_uri(imageUri); |
| if (!url) |
| { |
| return "None"; |
| } |
| std::string_view scheme = url->scheme(); |
| if (scheme == "smb") |
| { |
| return "CIFS"; |
| } |
| if (scheme == "https") |
| { |
| return "HTTPS"; |
| } |
| |
| return "None"; |
| } |
| |
| /** |
| * @brief Read all known properties from VM object interfaces |
| */ |
| inline void |
| vmParseInterfaceObject(const dbus::utility::DBusInteracesMap& interfaces, |
| const std::shared_ptr<bmcweb::AsyncResp>& aResp) |
| { |
| for (const auto& [interface, values] : interfaces) |
| { |
| if (interface == "xyz.openbmc_project.VirtualMedia.MountPoint") |
| { |
| for (const auto& [property, value] : values) |
| { |
| if (property == "EndpointId") |
| { |
| const std::string* endpointIdValue = |
| std::get_if<std::string>(&value); |
| if (endpointIdValue == nullptr) |
| { |
| continue; |
| } |
| if (!endpointIdValue->empty()) |
| { |
| // Proxy mode |
| aResp->res |
| .jsonValue["Oem"]["OpenBMC"]["WebSocketEndpoint"] = |
| *endpointIdValue; |
| aResp->res.jsonValue["TransferProtocolType"] = "OEM"; |
| } |
| } |
| if (property == "ImageURL") |
| { |
| const std::string* imageUrlValue = |
| std::get_if<std::string>(&value); |
| if (imageUrlValue != nullptr && !imageUrlValue->empty()) |
| { |
| std::filesystem::path filePath = *imageUrlValue; |
| if (!filePath.has_filename()) |
| { |
| // this will handle https share, which not |
| // necessarily has to have filename given. |
| aResp->res.jsonValue["ImageName"] = ""; |
| } |
| else |
| { |
| aResp->res.jsonValue["ImageName"] = |
| filePath.filename(); |
| } |
| |
| aResp->res.jsonValue["Image"] = *imageUrlValue; |
| aResp->res.jsonValue["TransferProtocolType"] = |
| getTransferProtocolTypeFromUri(*imageUrlValue); |
| |
| aResp->res.jsonValue["ConnectedVia"] = "URI"; |
| } |
| } |
| if (property == "WriteProtected") |
| { |
| const bool* writeProtectedValue = std::get_if<bool>(&value); |
| if (writeProtectedValue != nullptr) |
| { |
| aResp->res.jsonValue["WriteProtected"] = |
| *writeProtectedValue; |
| } |
| } |
| } |
| } |
| if (interface == "xyz.openbmc_project.VirtualMedia.Process") |
| { |
| for (const auto& [property, value] : values) |
| { |
| if (property == "Active") |
| { |
| const bool* activeValue = std::get_if<bool>(&value); |
| if (activeValue == nullptr) |
| { |
| BMCWEB_LOG_DEBUG << "Value Active not found"; |
| return; |
| } |
| aResp->res.jsonValue["Inserted"] = *activeValue; |
| |
| if (*activeValue) |
| { |
| aResp->res.jsonValue["ConnectedVia"] = "Applet"; |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * @brief Fill template for Virtual Media Item. |
| */ |
| inline nlohmann::json vmItemTemplate(const std::string& name, |
| const std::string& resName) |
| { |
| nlohmann::json item; |
| item["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Managers", name, "VirtualMedia", resName); |
| |
| item["@odata.type"] = "#VirtualMedia.v1_3_0.VirtualMedia"; |
| item["Name"] = "Virtual Removable Media"; |
| item["Id"] = resName; |
| item["WriteProtected"] = true; |
| item["MediaTypes"] = nlohmann::json::array_t({"CD", "USBStick"}); |
| item["TransferMethod"] = "Stream"; |
| item["Oem"]["OpenBMC"]["@odata.type"] = |
| "#OemVirtualMedia.v1_0_0.VirtualMedia"; |
| |
| return item; |
| } |
| |
| /** |
| * @brief Fills collection data |
| */ |
| inline void getVmResourceList(std::shared_ptr<bmcweb::AsyncResp> aResp, |
| const std::string& service, |
| const std::string& name) |
| { |
| BMCWEB_LOG_DEBUG << "Get available Virtual Media resources."; |
| crow::connections::systemBus->async_method_call( |
| [name, aResp{std::move(aResp)}]( |
| const boost::system::error_code ec, |
| const dbus::utility::ManagedObjectType& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error"; |
| return; |
| } |
| nlohmann::json& members = aResp->res.jsonValue["Members"]; |
| members = nlohmann::json::array(); |
| |
| for (const auto& object : subtree) |
| { |
| nlohmann::json item; |
| std::string path = object.first.filename(); |
| if (path.empty()) |
| { |
| continue; |
| } |
| |
| item["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Managers", name, "VirtualMedia", path); |
| members.emplace_back(std::move(item)); |
| } |
| aResp->res.jsonValue["Members@odata.count"] = members.size(); |
| }, |
| service, "/xyz/openbmc_project/VirtualMedia", |
| "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); |
| } |
| |
| /** |
| * @brief Fills data for specific resource |
| */ |
| inline void getVmData(const std::shared_ptr<bmcweb::AsyncResp>& aResp, |
| const std::string& service, const std::string& name, |
| const std::string& resName) |
| { |
| BMCWEB_LOG_DEBUG << "Get Virtual Media resource data."; |
| |
| crow::connections::systemBus->async_method_call( |
| [resName, name, |
| aResp](const boost::system::error_code ec, |
| const dbus::utility::ManagedObjectType& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error"; |
| |
| return; |
| } |
| |
| for (const auto& item : subtree) |
| { |
| std::string thispath = item.first.filename(); |
| if (thispath.empty()) |
| { |
| continue; |
| } |
| |
| if (thispath != resName) |
| { |
| continue; |
| } |
| |
| // "Legacy"/"Proxy" |
| auto mode = item.first.parent_path(); |
| // "VirtualMedia" |
| auto type = mode.parent_path(); |
| if (mode.filename().empty() || type.filename().empty()) |
| { |
| continue; |
| } |
| |
| if (type.filename() != "VirtualMedia") |
| { |
| continue; |
| } |
| |
| aResp->res.jsonValue = vmItemTemplate(name, resName); |
| |
| // Check if dbus path is Legacy type |
| if (mode.filename() == "Legacy") |
| { |
| aResp->res.jsonValue["Actions"]["#VirtualMedia.InsertMedia"] |
| ["target"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Managers", name, "VirtualMedia", resName, |
| "Actions", "VirtualMedia.InsertMedia"); |
| } |
| |
| vmParseInterfaceObject(item.second, aResp); |
| |
| aResp->res |
| .jsonValue["Actions"]["#VirtualMedia.EjectMedia"]["target"] = |
| crow::utility::urlFromPieces("redfish", "v1", "Managers", name, |
| "VirtualMedia", resName, "Actions", |
| "VirtualMedia.EjectMedia"); |
| return; |
| } |
| |
| messages::resourceNotFound(aResp->res, "VirtualMedia", resName); |
| }, |
| service, "/xyz/openbmc_project/VirtualMedia", |
| "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); |
| } |
| |
| /** |
| * @brief Transfer protocols supported for InsertMedia action. |
| * |
| */ |
| enum class TransferProtocol |
| { |
| https, |
| smb, |
| invalid |
| }; |
| |
| /** |
| * @brief Function extracts transfer protocol type from URI. |
| * |
| */ |
| inline std::optional<TransferProtocol> |
| getTransferProtocolFromUri(const boost::urls::url_view& imageUri) |
| { |
| std::string_view scheme = imageUri.scheme(); |
| if (scheme == "smb") |
| { |
| return TransferProtocol::smb; |
| } |
| if (scheme == "https") |
| { |
| return TransferProtocol::https; |
| } |
| if (!scheme.empty()) |
| { |
| return TransferProtocol::invalid; |
| } |
| |
| return {}; |
| } |
| |
| /** |
| * @brief Function convert transfer protocol from string param. |
| * |
| */ |
| inline std::optional<TransferProtocol> getTransferProtocolFromParam( |
| const std::optional<std::string>& transferProtocolType) |
| { |
| if (transferProtocolType == std::nullopt) |
| { |
| return {}; |
| } |
| |
| if (*transferProtocolType == "CIFS") |
| { |
| return TransferProtocol::smb; |
| } |
| |
| if (*transferProtocolType == "HTTPS") |
| { |
| return TransferProtocol::https; |
| } |
| |
| return TransferProtocol::invalid; |
| } |
| |
| /** |
| * @brief Function extends URI with transfer protocol type. |
| * |
| */ |
| inline std::string |
| getUriWithTransferProtocol(const std::string& imageUri, |
| const TransferProtocol& transferProtocol) |
| { |
| if (transferProtocol == TransferProtocol::smb) |
| { |
| return "smb://" + imageUri; |
| } |
| |
| if (transferProtocol == TransferProtocol::https) |
| { |
| return "https://" + imageUri; |
| } |
| |
| return imageUri; |
| } |
| |
| /** |
| * @brief Function validate parameters of insert media request. |
| * |
| */ |
| inline bool |
| validateParams(crow::Response& res, std::string& imageUrl, |
| const std::optional<bool>& inserted, |
| const std::optional<std::string>& transferMethod, |
| const std::optional<std::string>& transferProtocolType) |
| { |
| BMCWEB_LOG_DEBUG << "Validation started"; |
| // required param imageUrl must not be empty |
| if (imageUrl.empty()) |
| { |
| BMCWEB_LOG_ERROR << "Request action parameter Image is empty."; |
| |
| messages::propertyValueFormatError(res, "<empty>", "Image"); |
| |
| return false; |
| } |
| |
| // optional param inserted must be true |
| if ((inserted != std::nullopt) && !*inserted) |
| { |
| BMCWEB_LOG_ERROR |
| << "Request action optional parameter Inserted must be true."; |
| |
| messages::actionParameterNotSupported(res, "Inserted", "InsertMedia"); |
| |
| return false; |
| } |
| |
| // optional param transferMethod must be stream |
| if ((transferMethod != std::nullopt) && (*transferMethod != "Stream")) |
| { |
| BMCWEB_LOG_ERROR << "Request action optional parameter " |
| "TransferMethod must be Stream."; |
| |
| messages::actionParameterNotSupported(res, "TransferMethod", |
| "InsertMedia"); |
| |
| return false; |
| } |
| boost::urls::result<boost::urls::url_view> url = |
| boost::urls::parse_uri(imageUrl); |
| if (!url) |
| { |
| messages::actionParameterValueFormatError(res, imageUrl, "Image", |
| "InsertMedia"); |
| return false; |
| } |
| std::optional<TransferProtocol> uriTransferProtocolType = |
| getTransferProtocolFromUri(*url); |
| |
| std::optional<TransferProtocol> paramTransferProtocolType = |
| getTransferProtocolFromParam(transferProtocolType); |
| |
| // ImageUrl does not contain valid protocol type |
| if (*uriTransferProtocolType == TransferProtocol::invalid) |
| { |
| BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must " |
| "contain specified protocol type from list: " |
| "(smb, https)."; |
| |
| messages::resourceAtUriInUnknownFormat(res, *url); |
| |
| return false; |
| } |
| |
| // transferProtocolType should contain value from list |
| if (*paramTransferProtocolType == TransferProtocol::invalid) |
| { |
| BMCWEB_LOG_ERROR << "Request action parameter TransferProtocolType " |
| "must be provided with value from list: " |
| "(CIFS, HTTPS)."; |
| |
| messages::propertyValueNotInList(res, *transferProtocolType, |
| "TransferProtocolType"); |
| return false; |
| } |
| |
| // valid transfer protocol not provided either with URI nor param |
| if ((uriTransferProtocolType == std::nullopt) && |
| (paramTransferProtocolType == std::nullopt)) |
| { |
| BMCWEB_LOG_ERROR << "Request action parameter ImageUrl must " |
| "contain specified protocol type or param " |
| "TransferProtocolType must be provided."; |
| |
| messages::resourceAtUriInUnknownFormat(res, *url); |
| |
| return false; |
| } |
| |
| // valid transfer protocol provided both with URI and param |
| if ((paramTransferProtocolType != std::nullopt) && |
| (uriTransferProtocolType != std::nullopt)) |
| { |
| // check if protocol is the same for URI and param |
| if (*paramTransferProtocolType != *uriTransferProtocolType) |
| { |
| BMCWEB_LOG_ERROR << "Request action parameter " |
| "TransferProtocolType must contain the " |
| "same protocol type as protocol type " |
| "provided with param imageUrl."; |
| |
| messages::actionParameterValueTypeError(res, *transferProtocolType, |
| "TransferProtocolType", |
| "InsertMedia"); |
| |
| return false; |
| } |
| } |
| |
| // validation passed |
| // add protocol to URI if needed |
| if (uriTransferProtocolType == std::nullopt) |
| { |
| imageUrl = |
| getUriWithTransferProtocol(imageUrl, *paramTransferProtocolType); |
| } |
| |
| return true; |
| } |
| |
| template <typename T> |
| static void secureCleanup(T& value) |
| { |
| // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast) |
| auto raw = const_cast<typename T::value_type*>(value.data()); |
| explicit_bzero(raw, value.size() * sizeof(*raw)); |
| } |
| |
| class Credentials |
| { |
| public: |
| Credentials(std::string&& user, std::string&& password) : |
| userBuf(std::move(user)), passBuf(std::move(password)) |
| {} |
| |
| ~Credentials() |
| { |
| secureCleanup(userBuf); |
| secureCleanup(passBuf); |
| } |
| |
| const std::string& user() |
| { |
| return userBuf; |
| } |
| |
| const std::string& password() |
| { |
| return passBuf; |
| } |
| |
| Credentials() = delete; |
| Credentials(const Credentials&) = delete; |
| Credentials& operator=(const Credentials&) = delete; |
| Credentials(Credentials&&) = delete; |
| Credentials& operator=(Credentials&&) = delete; |
| |
| private: |
| std::string userBuf; |
| std::string passBuf; |
| }; |
| |
| class CredentialsProvider |
| { |
| public: |
| template <typename T> |
| struct Deleter |
| { |
| void operator()(T* buff) const |
| { |
| if (buff) |
| { |
| secureCleanup(*buff); |
| delete buff; |
| } |
| } |
| }; |
| |
| using Buffer = std::vector<char>; |
| using SecureBuffer = std::unique_ptr<Buffer, Deleter<Buffer>>; |
| // Using explicit definition instead of std::function to avoid implicit |
| // conversions eg. stack copy instead of reference |
| using FormatterFunc = void(const std::string& username, |
| const std::string& password, Buffer& dest); |
| |
| CredentialsProvider(std::string&& user, std::string&& password) : |
| credentials(std::move(user), std::move(password)) |
| {} |
| |
| const std::string& user() |
| { |
| return credentials.user(); |
| } |
| |
| const std::string& password() |
| { |
| return credentials.password(); |
| } |
| |
| SecureBuffer pack(FormatterFunc* formatter) |
| { |
| SecureBuffer packed{new Buffer{}}; |
| if (formatter != nullptr) |
| { |
| formatter(credentials.user(), credentials.password(), *packed); |
| } |
| |
| return packed; |
| } |
| |
| private: |
| Credentials credentials; |
| }; |
| |
| // Wrapper for boost::async_pipe ensuring proper pipe cleanup |
| template <typename Buffer> |
| class Pipe |
| { |
| public: |
| using unix_fd = sdbusplus::message::unix_fd; |
| |
| Pipe(boost::asio::io_context& io, Buffer&& bufferIn) : |
| impl(io), buffer{std::move(bufferIn)} |
| {} |
| |
| ~Pipe() |
| { |
| // Named pipe needs to be explicitly removed |
| impl.close(); |
| } |
| |
| Pipe(const Pipe&) = delete; |
| Pipe(Pipe&&) = delete; |
| Pipe& operator=(const Pipe&) = delete; |
| Pipe& operator=(Pipe&&) = delete; |
| |
| unix_fd fd() |
| { |
| return unix_fd{impl.native_source()}; |
| } |
| |
| template <typename WriteHandler> |
| void asyncWrite(WriteHandler&& handler) |
| { |
| impl.async_write_some(data(), std::forward<WriteHandler>(handler)); |
| } |
| |
| private: |
| // Specialization for pointer types |
| template <typename B = Buffer> |
| typename std::enable_if<boost::has_dereference<B>::value, |
| boost::asio::const_buffer>::type |
| data() |
| { |
| return boost::asio::buffer(*buffer); |
| } |
| |
| template <typename B = Buffer> |
| typename std::enable_if<!boost::has_dereference<B>::value, |
| boost::asio::const_buffer>::type |
| data() |
| { |
| return boost::asio::buffer(buffer); |
| } |
| |
| const std::string name; |
| boost::process::async_pipe impl; |
| Buffer buffer; |
| }; |
| |
| /** |
| * @brief Function transceives data with dbus directly. |
| * |
| * All BMC state properties will be retrieved before sending reset request. |
| */ |
| inline void doMountVmLegacy(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& service, const std::string& name, |
| const std::string& imageUrl, const bool rw, |
| std::string&& userName, std::string&& password) |
| { |
| using SecurePipe = Pipe<CredentialsProvider::SecureBuffer>; |
| constexpr const size_t secretLimit = 1024; |
| |
| std::shared_ptr<SecurePipe> secretPipe; |
| dbus::utility::DbusVariantType unixFd = -1; |
| |
| if (!userName.empty() || !password.empty()) |
| { |
| // Encapsulate in safe buffer |
| CredentialsProvider credentials(std::move(userName), |
| std::move(password)); |
| |
| // Payload must contain data + NULL delimiters |
| if (credentials.user().size() + credentials.password().size() + 2 > |
| secretLimit) |
| { |
| BMCWEB_LOG_ERROR << "Credentials too long to handle"; |
| messages::unrecognizedRequestBody(asyncResp->res); |
| return; |
| } |
| |
| // Pack secret |
| auto secret = credentials.pack( |
| [](const auto& user, const auto& pass, auto& buff) { |
| std::copy(user.begin(), user.end(), std::back_inserter(buff)); |
| buff.push_back('\0'); |
| std::copy(pass.begin(), pass.end(), std::back_inserter(buff)); |
| buff.push_back('\0'); |
| }); |
| |
| // Open pipe |
| secretPipe = std::make_shared<SecurePipe>( |
| crow::connections::systemBus->get_io_context(), std::move(secret)); |
| unixFd = secretPipe->fd(); |
| |
| // Pass secret over pipe |
| secretPipe->asyncWrite( |
| [asyncResp](const boost::system::error_code& ec, std::size_t) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "Failed to pass secret: " << ec; |
| messages::internalError(asyncResp->res); |
| } |
| }); |
| } |
| |
| crow::connections::systemBus->async_method_call( |
| [asyncResp, secretPipe](const boost::system::error_code ec, |
| bool success) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; |
| messages::internalError(asyncResp->res); |
| } |
| else if (!success) |
| { |
| BMCWEB_LOG_ERROR << "Service responded with error"; |
| messages::generalError(asyncResp->res); |
| } |
| }, |
| service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, |
| "xyz.openbmc_project.VirtualMedia.Legacy", "Mount", imageUrl, rw, |
| unixFd); |
| } |
| |
| /** |
| * @brief Function transceives data with dbus directly. |
| * |
| * All BMC state properties will be retrieved before sending reset request. |
| */ |
| inline void doVmAction(const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& service, const std::string& name, |
| bool legacy) |
| { |
| |
| // Legacy mount requires parameter with image |
| if (legacy) |
| { |
| crow::connections::systemBus->async_method_call( |
| [asyncResp](const boost::system::error_code ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; |
| |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| }, |
| service, "/xyz/openbmc_project/VirtualMedia/Legacy/" + name, |
| "xyz.openbmc_project.VirtualMedia.Legacy", "Unmount"); |
| } |
| else // proxy |
| { |
| crow::connections::systemBus->async_method_call( |
| [asyncResp](const boost::system::error_code ec) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "Bad D-Bus request error: " << ec; |
| |
| messages::internalError(asyncResp->res); |
| return; |
| } |
| }, |
| service, "/xyz/openbmc_project/VirtualMedia/Proxy/" + name, |
| "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); |
| } |
| } |
| |
| struct InsertMediaActionParams |
| { |
| std::string imageUrl; |
| std::optional<std::string> userName; |
| std::optional<std::string> password; |
| std::optional<std::string> transferMethod; |
| std::optional<std::string> transferProtocolType; |
| std::optional<bool> writeProtected = true; |
| std::optional<bool> inserted; |
| }; |
| |
| inline void handleManagersVirtualMediaActionInsertPost( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& name, const std::string& resName) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (name != "bmc") |
| { |
| messages::resourceNotFound(asyncResp->res, "VirtualMedia.Insert", |
| resName); |
| |
| return; |
| } |
| InsertMediaActionParams actionParams; |
| |
| // Read obligatory parameters (url of |
| // image) |
| if (!json_util::readJsonAction( |
| req, asyncResp->res, "Image", actionParams.imageUrl, |
| "WriteProtected", actionParams.writeProtected, "UserName", |
| actionParams.userName, "Password", actionParams.password, |
| "Inserted", actionParams.inserted, "TransferMethod", |
| actionParams.transferMethod, "TransferProtocolType", |
| actionParams.transferProtocolType)) |
| { |
| BMCWEB_LOG_DEBUG << "Image is not provided"; |
| return; |
| } |
| |
| bool paramsValid = validateParams( |
| asyncResp->res, actionParams.imageUrl, actionParams.inserted, |
| actionParams.transferMethod, actionParams.transferProtocolType); |
| |
| if (!paramsValid) |
| { |
| return; |
| } |
| |
| crow::connections::systemBus->async_method_call( |
| [asyncResp, actionParams, |
| resName](const boost::system::error_code ec, |
| const dbus::utility::MapperGetObject& getObjectType) mutable { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec; |
| messages::internalError(asyncResp->res); |
| |
| return; |
| } |
| std::string service = getObjectType.begin()->first; |
| BMCWEB_LOG_DEBUG << "GetObjectType: " << service; |
| |
| crow::connections::systemBus->async_method_call( |
| [service, resName, actionParams, |
| asyncResp](const boost::system::error_code ec2, |
| dbus::utility::ManagedObjectType& subtree) mutable { |
| if (ec2) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error"; |
| |
| return; |
| } |
| |
| for (const auto& object : subtree) |
| { |
| const std::string& path = |
| static_cast<const std::string&>(object.first); |
| |
| std::size_t lastIndex = path.rfind('/'); |
| if (lastIndex == std::string::npos) |
| { |
| continue; |
| } |
| |
| lastIndex += 1; |
| |
| if (path.substr(lastIndex) == resName) |
| { |
| lastIndex = path.rfind("Proxy"); |
| if (lastIndex != std::string::npos) |
| { |
| // Not possible in proxy mode |
| BMCWEB_LOG_DEBUG << "InsertMedia not " |
| "allowed in proxy mode"; |
| messages::resourceNotFound(asyncResp->res, |
| "VirtualMedia.InsertMedia", |
| resName); |
| |
| return; |
| } |
| |
| lastIndex = path.rfind("Legacy"); |
| if (lastIndex == std::string::npos) |
| { |
| continue; |
| } |
| |
| // manager is irrelevant for |
| // VirtualMedia dbus calls |
| doMountVmLegacy(asyncResp, service, resName, |
| actionParams.imageUrl, |
| !(*actionParams.writeProtected), |
| std::move(*actionParams.userName), |
| std::move(*actionParams.password)); |
| |
| return; |
| } |
| } |
| BMCWEB_LOG_DEBUG << "Parent item not found"; |
| messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); |
| }, |
| service, "/xyz/openbmc_project/VirtualMedia", |
| "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); |
| }, |
| "xyz.openbmc_project.ObjectMapper", |
| "/xyz/openbmc_project/object_mapper", |
| "xyz.openbmc_project.ObjectMapper", "GetObject", |
| "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>()); |
| } |
| |
| inline void handleManagersVirtualMediaActionEject( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& managerName, const std::string& resName) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (managerName != "bmc") |
| { |
| messages::resourceNotFound(asyncResp->res, "VirtualMedia.Eject", |
| resName); |
| |
| return; |
| } |
| |
| crow::connections::systemBus->async_method_call( |
| [asyncResp, |
| resName](const boost::system::error_code ec2, |
| const dbus::utility::MapperGetObject& getObjectType) { |
| if (ec2) |
| { |
| BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec2; |
| messages::internalError(asyncResp->res); |
| |
| return; |
| } |
| std::string service = getObjectType.begin()->first; |
| BMCWEB_LOG_DEBUG << "GetObjectType: " << service; |
| |
| crow::connections::systemBus->async_method_call( |
| [resName, service, asyncResp{asyncResp}]( |
| const boost::system::error_code ec, |
| const dbus::utility::ManagedObjectType& subtree) { |
| if (ec) |
| { |
| BMCWEB_LOG_DEBUG << "DBUS response error"; |
| |
| return; |
| } |
| |
| for (const auto& object : subtree) |
| { |
| const std::string& path = |
| static_cast<const std::string&>(object.first); |
| |
| std::size_t lastIndex = path.rfind('/'); |
| if (lastIndex == std::string::npos) |
| { |
| continue; |
| } |
| |
| lastIndex += 1; |
| |
| if (path.substr(lastIndex) == resName) |
| { |
| lastIndex = path.rfind("Proxy"); |
| if (lastIndex != std::string::npos) |
| { |
| // Proxy mode |
| doVmAction(asyncResp, service, resName, false); |
| } |
| |
| lastIndex = path.rfind("Legacy"); |
| if (lastIndex != std::string::npos) |
| { |
| // Legacy mode |
| doVmAction(asyncResp, service, resName, true); |
| } |
| |
| return; |
| } |
| } |
| BMCWEB_LOG_DEBUG << "Parent item not found"; |
| messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); |
| }, |
| service, "/xyz/openbmc_project/VirtualMedia", |
| "org.freedesktop.DBus.ObjectManager", "GetManagedObjects"); |
| }, |
| "xyz.openbmc_project.ObjectMapper", |
| "/xyz/openbmc_project/object_mapper", |
| "xyz.openbmc_project.ObjectMapper", "GetObject", |
| "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>()); |
| } |
| |
| inline void handleManagersVirtualMediaCollectionGet( |
| crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& name) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (name != "bmc") |
| { |
| messages::resourceNotFound(asyncResp->res, "VirtualMedia", name); |
| |
| return; |
| } |
| |
| asyncResp->res.jsonValue["@odata.type"] = |
| "#VirtualMediaCollection.VirtualMediaCollection"; |
| asyncResp->res.jsonValue["Name"] = "Virtual Media Services"; |
| asyncResp->res.jsonValue["@odata.id"] = crow::utility::urlFromPieces( |
| "redfish", "v1", "Managers", name, "VirtualMedia"); |
| |
| crow::connections::systemBus->async_method_call( |
| [asyncResp, name](const boost::system::error_code ec, |
| const dbus::utility::MapperGetObject& getObjectType) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec; |
| messages::internalError(asyncResp->res); |
| |
| return; |
| } |
| std::string service = getObjectType.begin()->first; |
| BMCWEB_LOG_DEBUG << "GetObjectType: " << service; |
| |
| getVmResourceList(asyncResp, service, name); |
| }, |
| "xyz.openbmc_project.ObjectMapper", |
| "/xyz/openbmc_project/object_mapper", |
| "xyz.openbmc_project.ObjectMapper", "GetObject", |
| "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>()); |
| } |
| |
| inline void |
| handleVirtualMediaGet(crow::App& app, const crow::Request& req, |
| const std::shared_ptr<bmcweb::AsyncResp>& asyncResp, |
| const std::string& name, const std::string& resName) |
| { |
| if (!redfish::setUpRedfishRoute(app, req, asyncResp)) |
| { |
| return; |
| } |
| if (name != "bmc") |
| { |
| messages::resourceNotFound(asyncResp->res, "VirtualMedia", resName); |
| |
| return; |
| } |
| |
| crow::connections::systemBus->async_method_call( |
| [asyncResp, name, |
| resName](const boost::system::error_code ec, |
| const dbus::utility::MapperGetObject& getObjectType) { |
| if (ec) |
| { |
| BMCWEB_LOG_ERROR << "ObjectMapper::GetObject call failed: " << ec; |
| messages::internalError(asyncResp->res); |
| |
| return; |
| } |
| std::string service = getObjectType.begin()->first; |
| BMCWEB_LOG_DEBUG << "GetObjectType: " << service; |
| |
| getVmData(asyncResp, service, name, resName); |
| }, |
| "xyz.openbmc_project.ObjectMapper", |
| "/xyz/openbmc_project/object_mapper", |
| "xyz.openbmc_project.ObjectMapper", "GetObject", |
| "/xyz/openbmc_project/VirtualMedia", std::array<const char*, 0>()); |
| } |
| |
| inline void requestNBDVirtualMediaRoutes(App& app) |
| { |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.InsertMedia") |
| .privileges(redfish::privileges::postVirtualMedia) |
| .methods(boost::beast::http::verb::post)(std::bind_front( |
| handleManagersVirtualMediaActionInsertPost, std::ref(app))); |
| |
| BMCWEB_ROUTE( |
| app, |
| "/redfish/v1/Managers/<str>/VirtualMedia/<str>/Actions/VirtualMedia.EjectMedia") |
| .privileges(redfish::privileges::postVirtualMedia) |
| .methods(boost::beast::http::verb::post)(std::bind_front( |
| handleManagersVirtualMediaActionEject, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/") |
| .privileges(redfish::privileges::getVirtualMediaCollection) |
| .methods(boost::beast::http::verb::get)(std::bind_front( |
| handleManagersVirtualMediaCollectionGet, std::ref(app))); |
| |
| BMCWEB_ROUTE(app, "/redfish/v1/Managers/<str>/VirtualMedia/<str>/") |
| .privileges(redfish::privileges::getVirtualMedia) |
| .methods(boost::beast::http::verb::get)( |
| std::bind_front(handleVirtualMediaGet, std::ref(app))); |
| } |
| |
| } // namespace redfish |