diff options
Diffstat (limited to 'include/nbd_proxy.hpp')
-rw-r--r-- | include/nbd_proxy.hpp | 387 |
1 files changed, 0 insertions, 387 deletions
diff --git a/include/nbd_proxy.hpp b/include/nbd_proxy.hpp deleted file mode 100644 index adc51a85ef..0000000000 --- a/include/nbd_proxy.hpp +++ /dev/null @@ -1,387 +0,0 @@ -/* -// Copyright (c) 2019 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 "app.hpp" -#include "dbus_utility.hpp" -#include "privileges.hpp" -#include "websocket.hpp" - -#include <boost/asio/local/stream_protocol.hpp> -#include <boost/beast/core/buffers_to_string.hpp> -#include <boost/container/flat_map.hpp> - -#include <string_view> - -namespace crow -{ - -namespace nbd_proxy -{ - -using boost::asio::local::stream_protocol; - -static constexpr size_t nbdBufferSize = 131088; -constexpr const char* requiredPrivilegeString = "ConfigureManager"; - -struct NbdProxyServer : std::enable_shared_from_this<NbdProxyServer> -{ - NbdProxyServer(crow::websocket::Connection& connIn, - const std::string& socketIdIn, - const std::string& endpointIdIn, const std::string& pathIn) : - socketId(socketIdIn), - endpointId(endpointIdIn), path(pathIn), - - peerSocket(connIn.getIoContext()), - acceptor(connIn.getIoContext(), stream_protocol::endpoint(socketId)), - connection(connIn) - {} - - NbdProxyServer(const NbdProxyServer&) = delete; - NbdProxyServer(NbdProxyServer&&) = delete; - NbdProxyServer& operator=(const NbdProxyServer&) = delete; - NbdProxyServer& operator=(NbdProxyServer&&) = delete; - - ~NbdProxyServer() - { - BMCWEB_LOG_DEBUG("NbdProxyServer destructor"); - - BMCWEB_LOG_DEBUG("peerSocket->close()"); - boost::system::error_code ec; - peerSocket.close(ec); - - BMCWEB_LOG_DEBUG("std::filesystem::remove({})", socketId); - std::error_code ec2; - std::filesystem::remove(socketId.c_str(), ec2); - if (ec2) - { - BMCWEB_LOG_DEBUG("Failed to remove file, ignoring"); - } - - crow::connections::systemBus->async_method_call( - dbus::utility::logError, "xyz.openbmc_project.VirtualMedia", path, - "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount"); - } - - std::string getEndpointId() const - { - return endpointId; - } - - void run() - { - acceptor.async_accept( - [weak(weak_from_this())](const boost::system::error_code& ec, - stream_protocol::socket socket) { - if (ec) - { - BMCWEB_LOG_ERROR("UNIX socket: async_accept error = {}", - ec.message()); - return; - } - - BMCWEB_LOG_DEBUG("Connection opened"); - std::shared_ptr<NbdProxyServer> self = weak.lock(); - if (self == nullptr) - { - return; - } - - self->connection.resumeRead(); - self->peerSocket = std::move(socket); - // Start reading from socket - self->doRead(); - }); - - auto mountHandler = [weak(weak_from_this())]( - const boost::system::error_code& ec, bool) { - std::shared_ptr<NbdProxyServer> self = weak.lock(); - if (self == nullptr) - { - return; - } - if (ec) - { - BMCWEB_LOG_ERROR("DBus error: cannot call mount method = {}", - ec.message()); - - self->connection.close("Failed to mount media"); - return; - } - }; - - crow::connections::systemBus->async_method_call( - std::move(mountHandler), "xyz.openbmc_project.VirtualMedia", path, - "xyz.openbmc_project.VirtualMedia.Proxy", "Mount"); - } - - void send(std::string_view buffer, std::function<void()>&& onDone) - { - size_t copied = boost::asio::buffer_copy( - ws2uxBuf.prepare(buffer.size()), boost::asio::buffer(buffer)); - ws2uxBuf.commit(copied); - - doWrite(std::move(onDone)); - } - - private: - void doRead() - { - // Trigger async read - peerSocket.async_read_some( - ux2wsBuf.prepare(nbdBufferSize), - [weak(weak_from_this())](const boost::system::error_code& ec, - size_t bytesRead) { - if (ec) - { - BMCWEB_LOG_ERROR("UNIX socket: async_read_some error = {}", - ec.message()); - return; - } - std::shared_ptr<NbdProxyServer> self = weak.lock(); - if (self == nullptr) - { - return; - } - - // Send to websocket - self->ux2wsBuf.commit(bytesRead); - self->connection.sendEx( - crow::websocket::MessageType::Binary, - boost::beast::buffers_to_string(self->ux2wsBuf.data()), - [weak(self->weak_from_this())]() { - std::shared_ptr<NbdProxyServer> self2 = weak.lock(); - if (self2 != nullptr) - { - self2->ux2wsBuf.consume(self2->ux2wsBuf.size()); - self2->doRead(); - } - }); - }); - } - - void doWrite(std::function<void()>&& onDone) - { - if (uxWriteInProgress) - { - BMCWEB_LOG_ERROR("Write in progress"); - return; - } - - if (ws2uxBuf.size() == 0) - { - BMCWEB_LOG_ERROR("No data to write to UNIX socket"); - return; - } - - uxWriteInProgress = true; - peerSocket.async_write_some( - ws2uxBuf.data(), - [weak(weak_from_this()), - onDone(std::move(onDone))](const boost::system::error_code& ec, - size_t bytesWritten) mutable { - std::shared_ptr<NbdProxyServer> self = weak.lock(); - if (self == nullptr) - { - return; - } - - self->ws2uxBuf.consume(bytesWritten); - self->uxWriteInProgress = false; - - if (ec) - { - BMCWEB_LOG_ERROR("UNIX: async_write error = {}", ec.message()); - self->connection.close("Internal error"); - return; - } - - // Retrigger doWrite if there is something in buffer - if (self->ws2uxBuf.size() > 0) - { - self->doWrite(std::move(onDone)); - return; - } - onDone(); - }); - } - - // Keeps UNIX socket endpoint file path - const std::string socketId; - const std::string endpointId; - const std::string path; - - bool uxWriteInProgress = false; - - // UNIX => WebSocket buffer - boost::beast::flat_static_buffer<nbdBufferSize> ux2wsBuf; - - // WebSocket => UNIX buffer - boost::beast::flat_static_buffer<nbdBufferSize> ws2uxBuf; - - // The socket used to communicate with the client. - stream_protocol::socket peerSocket; - - // Default acceptor for UNIX socket - stream_protocol::acceptor acceptor; - - crow::websocket::Connection& connection; -}; - -using SessionMap = boost::container::flat_map<crow::websocket::Connection*, - std::shared_ptr<NbdProxyServer>>; -// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables) -static SessionMap sessions; - -inline void - afterGetManagedObjects(crow::websocket::Connection& conn, - const boost::system::error_code& ec, - const dbus::utility::ManagedObjectType& objects) -{ - const std::string* socketValue = nullptr; - const std::string* endpointValue = nullptr; - const std::string* endpointObjectPath = nullptr; - - if (ec) - { - BMCWEB_LOG_ERROR("DBus error: {}", ec.message()); - conn.close("Failed to create mount point"); - return; - } - - for (const auto& [objectPath, interfaces] : objects) - { - for (const auto& [interface, properties] : interfaces) - { - if (interface != "xyz.openbmc_project.VirtualMedia.MountPoint") - { - continue; - } - - for (const auto& [name, value] : properties) - { - if (name == "EndpointId") - { - endpointValue = std::get_if<std::string>(&value); - - if (endpointValue == nullptr) - { - BMCWEB_LOG_ERROR("EndpointId property value is null"); - } - } - if (name == "Socket") - { - socketValue = std::get_if<std::string>(&value); - if (socketValue == nullptr) - { - BMCWEB_LOG_ERROR("Socket property value is null"); - } - } - } - } - - if ((endpointValue != nullptr) && (socketValue != nullptr) && - *endpointValue == conn.url().path()) - { - endpointObjectPath = &objectPath.str; - break; - } - } - - if (objects.empty() || endpointObjectPath == nullptr) - { - BMCWEB_LOG_ERROR("Cannot find requested EndpointId"); - conn.close("Failed to match EndpointId"); - return; - } - - for (const auto& session : sessions) - { - if (session.second->getEndpointId() == conn.url().path()) - { - BMCWEB_LOG_ERROR("Cannot open new connection - socket is in use"); - conn.close("Slot is in use"); - return; - } - } - - // If the socket file exists (i.e. after bmcweb crash), - // we cannot reuse it. - std::remove((*socketValue).c_str()); - - sessions[&conn] = std::make_shared<NbdProxyServer>( - conn, *socketValue, *endpointValue, *endpointObjectPath); - - sessions[&conn]->run(); -}; -inline void onOpen(crow::websocket::Connection& conn) -{ - BMCWEB_LOG_DEBUG("nbd-proxy.onopen({})", logPtr(&conn)); - - sdbusplus::message::object_path path("/xyz/openbmc_project/VirtualMedia"); - dbus::utility::getManagedObjects( - "xyz.openbmc_project.VirtualMedia", path, - [&conn](const boost::system::error_code& ec, - const dbus::utility::ManagedObjectType& objects) { - afterGetManagedObjects(conn, ec, objects); - }); - - // We need to wait for dbus and the websockets to hook up before data is - // sent/received. Tell the core to hold off messages until the sockets are - // up - conn.deferRead(); -} - -inline void onClose(crow::websocket::Connection& conn, - const std::string& reason) -{ - BMCWEB_LOG_DEBUG("nbd-proxy.onclose(reason = '{}')", reason); - auto session = sessions.find(&conn); - if (session == sessions.end()) - { - BMCWEB_LOG_DEBUG("No session to close"); - return; - } - // Remove reference to session in global map - sessions.erase(session); -} - -inline void onMessage(crow::websocket::Connection& conn, std::string_view data, - crow::websocket::MessageType /*type*/, - std::function<void()>&& whenComplete) -{ - BMCWEB_LOG_DEBUG("nbd-proxy.onMessage(len = {})", data.size()); - - // Acquire proxy from sessions - auto session = sessions.find(&conn); - if (session == sessions.end() || session->second == nullptr) - { - whenComplete(); - return; - } - - session->second->send(data, std::move(whenComplete)); -} - -inline void requestRoutes(App& app) -{ - BMCWEB_ROUTE(app, "/nbd/<str>") - .websocket() - .onopen(onOpen) - .onclose(onClose) - .onmessageex(onMessage); -} -} // namespace nbd_proxy -} // namespace crow |