blob: 95cb982655432252be1d088426a6e40c033f1c2f [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -06003#pragma once
4
Ed Tanousd7857202025-01-28 15:32:26 -08005#include "bmcweb_config.h"
6
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007#include "app.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08008#include "dbus_singleton.hpp"
Ed Tanous36c0f2a2024-02-09 13:50:26 -08009#include "dbus_utility.hpp"
Ed Tanousd98a2f92025-02-06 17:36:31 -080010#include "io_context_singleton.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080011#include "logging.hpp"
Ed Tanousfaf100f2023-05-25 10:03:14 -070012#include "websocket.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080013
Ed Tanousd7857202025-01-28 15:32:26 -080014#include <boost/asio/buffer.hpp>
15#include <boost/asio/error.hpp>
16#include <boost/asio/io_context.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080017#include <boost/asio/local/stream_protocol.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080018#include <boost/asio/readable_pipe.hpp>
19#include <boost/asio/writable_pipe.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080020#include <boost/beast/core/buffers_to_string.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080021#include <boost/beast/core/error.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060022#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080023#include <boost/container/flat_map.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080024#include <boost/process/v2/process.hpp>
25#include <boost/process/v2/stdio.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080026#include <sdbusplus/message/native_types.hpp>
27#include <sdbusplus/unpack_properties.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060028
Ed Tanousd7857202025-01-28 15:32:26 -080029#include <cerrno>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050030#include <csignal>
Ed Tanousd7857202025-01-28 15:32:26 -080031#include <cstddef>
32#include <filesystem>
33#include <format>
34#include <functional>
35#include <memory>
36#include <string>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080037#include <string_view>
Ed Tanousd7857202025-01-28 15:32:26 -080038#include <system_error>
39#include <utility>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050040
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060041namespace crow
42{
Ed Tanous36c0f2a2024-02-09 13:50:26 -080043
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060044namespace obmc_vm
45{
46
Ed Tanouscf9e4172022-12-21 09:30:16 -080047// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060048static crow::websocket::Connection* session = nullptr;
49
50// The max network block device buffer size is 128kb plus 16bytes
51// for the message header:
52// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
Troy Lee4ee8f0b2021-08-02 11:08:26 +080053static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060054
55class Handler : public std::enable_shared_from_this<Handler>
56{
57 public:
Ed Tanous3bfa3b22024-01-31 12:18:03 -080058 Handler(const std::string& media, boost::asio::io_context& ios) :
59 pipeOut(ios), pipeIn(ios),
60 proxy(ios, "/usr/bin/nbd-proxy", {media},
61 boost::process::v2::process_stdio{
Ed Tanous3515fd12024-12-11 23:34:32 -080062 .in = pipeIn, .out = pipeOut, .err = nullptr})
Gunnar Mills1214b7e2020-06-04 10:11:30 -050063 {}
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060064
Ed Tanous0c0084a2019-10-24 15:57:51 -070065 ~Handler() = default;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060066
Ed Tanousecd6a3a2022-01-07 09:18:40 -080067 Handler(const Handler&) = delete;
68 Handler(Handler&&) = delete;
69 Handler& operator=(const Handler&) = delete;
70 Handler& operator=(Handler&&) = delete;
71
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060072 void doClose()
73 {
74 // boost::process::child::terminate uses SIGKILL, need to send SIGTERM
75 // to allow the proxy to stop nbd-client and the USB device gadget.
Myung Bae92e11bf2025-01-31 09:22:23 -050076 // NOLINTNEXTLINE(misc-include-cleaner)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060077 int rc = kill(proxy.id(), SIGTERM);
Ed Tanouse662eae2022-01-25 10:39:19 -080078 if (rc != 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060079 {
Ed Tanous62598e32023-07-17 17:06:25 -070080 BMCWEB_LOG_ERROR("Failed to terminate nbd-proxy: {}", errno);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060081 return;
82 }
Troy Lee36ecbf32021-08-17 18:15:28 +080083
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060084 proxy.wait();
85 }
86
87 void connect()
88 {
89 std::error_code ec;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060090 if (ec)
91 {
Ed Tanous62598e32023-07-17 17:06:25 -070092 BMCWEB_LOG_ERROR("Couldn't connect to nbd-proxy: {}", ec.message());
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060093 if (session != nullptr)
94 {
95 session->close("Error connecting to nbd-proxy");
96 }
97 return;
98 }
99 doWrite();
100 doRead();
101 }
102
103 void doWrite()
104 {
105 if (doingWrite)
106 {
Ed Tanous62598e32023-07-17 17:06:25 -0700107 BMCWEB_LOG_DEBUG("Already writing. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600108 return;
109 }
110
Ed Tanous3515fd12024-12-11 23:34:32 -0800111 if (inputBuffer.size() == 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600112 {
Ed Tanous62598e32023-07-17 17:06:25 -0700113 BMCWEB_LOG_DEBUG("inputBuffer empty. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600114 return;
115 }
116
117 doingWrite = true;
118 pipeIn.async_write_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800119 inputBuffer.data(),
Ed Tanous81c4e332023-05-18 10:30:34 -0700120 [this, self(shared_from_this())](const boost::beast::error_code& ec,
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600121 std::size_t bytesWritten) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400122 BMCWEB_LOG_DEBUG("Wrote {}bytes", bytesWritten);
123 doingWrite = false;
Ed Tanous3515fd12024-12-11 23:34:32 -0800124 inputBuffer.consume(bytesWritten);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600125
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400126 if (session == nullptr)
127 {
128 return;
129 }
130 if (ec == boost::asio::error::eof)
131 {
132 session->close("VM socket port closed");
133 return;
134 }
135 if (ec)
136 {
137 session->close("Error in writing to proxy port");
138 BMCWEB_LOG_ERROR("Error in VM socket write {}", ec);
139 return;
140 }
141 doWrite();
142 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600143 }
144
145 void doRead()
146 {
Ed Tanous3515fd12024-12-11 23:34:32 -0800147 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600148
149 pipeOut.async_read_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800150 outputBuffer.prepare(bytes),
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600151 [this, self(shared_from_this())](
152 const boost::system::error_code& ec, std::size_t bytesRead) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400153 BMCWEB_LOG_DEBUG("Read done. Read {} bytes", bytesRead);
154 if (ec)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600155 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400156 BMCWEB_LOG_ERROR("Couldn't read from VM port: {}", ec);
157 if (session != nullptr)
158 {
159 session->close("Error in connecting to VM port");
160 }
161 return;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600162 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400163 if (session == nullptr)
164 {
165 return;
166 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600167
Ed Tanous3515fd12024-12-11 23:34:32 -0800168 outputBuffer.commit(bytesRead);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400169 std::string_view payload(
Ed Tanous3515fd12024-12-11 23:34:32 -0800170 static_cast<const char*>(outputBuffer.data().data()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400171 bytesRead);
172 session->sendBinary(payload);
Ed Tanous3515fd12024-12-11 23:34:32 -0800173 outputBuffer.consume(bytesRead);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600174
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400175 doRead();
176 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600177 }
178
Ed Tanous3bfa3b22024-01-31 12:18:03 -0800179 boost::asio::readable_pipe pipeOut;
180 boost::asio::writable_pipe pipeIn;
181 boost::process::v2::process proxy;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800182 bool doingWrite{false};
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600183
Ed Tanous3515fd12024-12-11 23:34:32 -0800184 boost::beast::flat_static_buffer<nbdBufferSize> outputBuffer;
185 boost::beast::flat_static_buffer<nbdBufferSize> inputBuffer;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600186};
187
Ed Tanouscf9e4172022-12-21 09:30:16 -0800188// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600189static std::shared_ptr<Handler> handler;
190
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800191} // namespace obmc_vm
192
193namespace nbd_proxy
194{
195using boost::asio::local::stream_protocol;
196
197// The max network block device buffer size is 128kb plus 16bytes
198// for the message header:
199// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
200static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
201
202struct NbdProxyServer : std::enable_shared_from_this<NbdProxyServer>
203{
204 NbdProxyServer(crow::websocket::Connection& connIn,
205 const std::string& socketIdIn,
206 const std::string& endpointIdIn, const std::string& pathIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400207 socketId(socketIdIn), endpointId(endpointIdIn), path(pathIn),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800208
Ed Tanousd98a2f92025-02-06 17:36:31 -0800209 peerSocket(getIoContext()),
210 acceptor(getIoContext(), stream_protocol::endpoint(socketId)),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800211 connection(connIn)
212 {}
213
214 NbdProxyServer(const NbdProxyServer&) = delete;
215 NbdProxyServer(NbdProxyServer&&) = delete;
216 NbdProxyServer& operator=(const NbdProxyServer&) = delete;
217 NbdProxyServer& operator=(NbdProxyServer&&) = delete;
218
219 ~NbdProxyServer()
220 {
221 BMCWEB_LOG_DEBUG("NbdProxyServer destructor");
222
223 BMCWEB_LOG_DEBUG("peerSocket->close()");
224 boost::system::error_code ec;
225 peerSocket.close(ec);
226
227 BMCWEB_LOG_DEBUG("std::filesystem::remove({})", socketId);
228 std::error_code ec2;
229 std::filesystem::remove(socketId.c_str(), ec2);
230 if (ec2)
231 {
232 BMCWEB_LOG_DEBUG("Failed to remove file, ignoring");
233 }
234
235 crow::connections::systemBus->async_method_call(
236 dbus::utility::logError, "xyz.openbmc_project.VirtualMedia", path,
237 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
238 }
239
240 std::string getEndpointId() const
241 {
242 return endpointId;
243 }
244
Ed Tanouse4b32752024-02-09 18:56:29 -0800245 static void afterMount(const std::weak_ptr<NbdProxyServer>& weak,
246 const boost::system::error_code& ec,
247 bool /*isBinary*/)
248 {
249 std::shared_ptr<NbdProxyServer> self = weak.lock();
250 if (self == nullptr)
251 {
252 return;
253 }
254 if (ec)
255 {
256 BMCWEB_LOG_ERROR("DBus error: cannot call mount method = {}",
257 ec.message());
258
259 self->connection.close("Failed to mount media");
260 return;
261 }
262 }
263
264 static void afterAccept(const std::weak_ptr<NbdProxyServer>& weak,
265 const boost::system::error_code& ec,
266 stream_protocol::socket socket)
267 {
268 if (ec)
269 {
270 BMCWEB_LOG_ERROR("UNIX socket: async_accept error = {}",
271 ec.message());
272 return;
273 }
274
275 BMCWEB_LOG_DEBUG("Connection opened");
276 std::shared_ptr<NbdProxyServer> self = weak.lock();
277 if (self == nullptr)
278 {
279 return;
280 }
281
282 self->connection.resumeRead();
283 self->peerSocket = std::move(socket);
284 // Start reading from socket
285 self->doRead();
286 }
287
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800288 void run()
289 {
290 acceptor.async_accept(
Ed Tanouse4b32752024-02-09 18:56:29 -0800291 std::bind_front(&NbdProxyServer::afterAccept, weak_from_this()));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800292
293 crow::connections::systemBus->async_method_call(
Ed Tanouse4b32752024-02-09 18:56:29 -0800294 [weak{weak_from_this()}](const boost::system::error_code& ec,
295 bool isBinary) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400296 afterMount(weak, ec, isBinary);
297 },
Ed Tanouse4b32752024-02-09 18:56:29 -0800298 "xyz.openbmc_project.VirtualMedia", path,
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800299 "xyz.openbmc_project.VirtualMedia.Proxy", "Mount");
300 }
301
302 void send(std::string_view buffer, std::function<void()>&& onDone)
303 {
304 size_t copied = boost::asio::buffer_copy(
305 ws2uxBuf.prepare(buffer.size()), boost::asio::buffer(buffer));
306 ws2uxBuf.commit(copied);
307
308 doWrite(std::move(onDone));
309 }
310
311 private:
Ed Tanouse4b32752024-02-09 18:56:29 -0800312 static void afterSendEx(const std::weak_ptr<NbdProxyServer>& weak)
313 {
314 std::shared_ptr<NbdProxyServer> self2 = weak.lock();
315 if (self2 != nullptr)
316 {
317 self2->ux2wsBuf.consume(self2->ux2wsBuf.size());
318 self2->doRead();
319 }
320 }
321
Ed Tanouse43512a2024-08-06 10:50:33 -0700322 void afterRead(const std::weak_ptr<NbdProxyServer>& weak,
323 const boost::system::error_code& ec, size_t bytesRead)
Ed Tanouse4b32752024-02-09 18:56:29 -0800324 {
325 if (ec)
326 {
327 BMCWEB_LOG_ERROR("UNIX socket: async_read_some error = {}",
328 ec.message());
329 return;
330 }
331 std::shared_ptr<NbdProxyServer> self = weak.lock();
332 if (self == nullptr)
333 {
334 return;
335 }
336
337 // Send to websocket
338 self->ux2wsBuf.commit(bytesRead);
339 self->connection.sendEx(
340 crow::websocket::MessageType::Binary,
341 boost::beast::buffers_to_string(self->ux2wsBuf.data()),
342 std::bind_front(&NbdProxyServer::afterSendEx, weak_from_this()));
343 }
344
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800345 void doRead()
346 {
347 // Trigger async read
Ed Tanouse4b32752024-02-09 18:56:29 -0800348 peerSocket.async_read_some(ux2wsBuf.prepare(nbdBufferSize),
349 std::bind_front(&NbdProxyServer::afterRead,
350 this, weak_from_this()));
351 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800352
Ed Tanouse4b32752024-02-09 18:56:29 -0800353 static void afterWrite(const std::weak_ptr<NbdProxyServer>& weak,
354 std::function<void()>&& onDone,
355 const boost::system::error_code& ec,
356 size_t bytesWritten)
357 {
358 std::shared_ptr<NbdProxyServer> self = weak.lock();
359 if (self == nullptr)
360 {
361 return;
362 }
363
364 self->ws2uxBuf.consume(bytesWritten);
365 self->uxWriteInProgress = false;
366
367 if (ec)
368 {
369 BMCWEB_LOG_ERROR("UNIX: async_write error = {}", ec.message());
370 self->connection.close("Internal error");
371 return;
372 }
373
374 // Retrigger doWrite if there is something in buffer
375 if (self->ws2uxBuf.size() > 0)
376 {
377 self->doWrite(std::move(onDone));
378 return;
379 }
380 onDone();
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800381 }
382
383 void doWrite(std::function<void()>&& onDone)
384 {
385 if (uxWriteInProgress)
386 {
387 BMCWEB_LOG_ERROR("Write in progress");
388 return;
389 }
390
391 if (ws2uxBuf.size() == 0)
392 {
393 BMCWEB_LOG_ERROR("No data to write to UNIX socket");
394 return;
395 }
396
397 uxWriteInProgress = true;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400398 peerSocket.async_write_some(
399 ws2uxBuf.data(),
400 std::bind_front(&NbdProxyServer::afterWrite, weak_from_this(),
401 std::move(onDone)));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800402 }
403
404 // Keeps UNIX socket endpoint file path
405 const std::string socketId;
406 const std::string endpointId;
407 const std::string path;
408
409 bool uxWriteInProgress = false;
410
411 // UNIX => WebSocket buffer
412 boost::beast::flat_static_buffer<nbdBufferSize> ux2wsBuf;
413
414 // WebSocket => UNIX buffer
415 boost::beast::flat_static_buffer<nbdBufferSize> ws2uxBuf;
416
417 // The socket used to communicate with the client.
418 stream_protocol::socket peerSocket;
419
420 // Default acceptor for UNIX socket
421 stream_protocol::acceptor acceptor;
422
423 crow::websocket::Connection& connection;
424};
425
426using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
427 std::shared_ptr<NbdProxyServer>>;
428// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
429static SessionMap sessions;
430
Patrick Williams504af5a2025-02-03 14:29:03 -0500431inline void afterGetSocket(
432 crow::websocket::Connection& conn,
433 const sdbusplus::message::object_path& path,
434 const boost::system::error_code& ec,
435 const dbus::utility::DBusPropertiesMap& propertiesList)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800436{
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800437 if (ec)
438 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200439 BMCWEB_LOG_ERROR("DBus getAllProperties error: {}", ec.message());
Ed Tanouse4b32752024-02-09 18:56:29 -0800440 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800441 return;
442 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800443 std::string endpointId;
444 std::string socket;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800445
Ed Tanouse4b32752024-02-09 18:56:29 -0800446 bool success = sdbusplus::unpackPropertiesNoThrow(
447 redfish::dbus_utils::UnpackErrorPrinter(), propertiesList, "EndpointId",
448 endpointId, "Socket", socket);
449
450 if (!success)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800451 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200452 BMCWEB_LOG_ERROR("Failed to unpack properties");
Ed Tanouse4b32752024-02-09 18:56:29 -0800453 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800454 return;
455 }
456
457 for (const auto& session : sessions)
458 {
459 if (session.second->getEndpointId() == conn.url().path())
460 {
461 BMCWEB_LOG_ERROR("Cannot open new connection - socket is in use");
462 conn.close("Slot is in use");
463 return;
464 }
465 }
466
467 // If the socket file exists (i.e. after bmcweb crash),
468 // we cannot reuse it.
Ed Tanous80ba22f2024-05-06 15:37:17 -0700469 std::error_code ec2;
470 std::filesystem::remove(socket.c_str(), ec2);
471 // Ignore failures. File might not exist.
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800472
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400473 sessions[&conn] =
474 std::make_shared<NbdProxyServer>(conn, socket, endpointId, path);
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800475 sessions[&conn]->run();
Ed Tanouse4b32752024-02-09 18:56:29 -0800476}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800477
478inline void onOpen(crow::websocket::Connection& conn)
479{
480 BMCWEB_LOG_DEBUG("nbd-proxy.onopen({})", logPtr(&conn));
481
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200482 if (conn.url().segments().size() < 2)
483 {
484 BMCWEB_LOG_ERROR("Invalid path - \"{}\"", conn.url().path());
485 conn.close("Internal error");
486 return;
487 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800488
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200489 std::string index = conn.url().segments().back();
490 std::string path =
491 std::format("/xyz/openbmc_project/VirtualMedia/Proxy/Slot_{}", index);
Ed Tanouse4b32752024-02-09 18:56:29 -0800492
Ed Tanousdeae6a72024-11-11 21:58:57 -0800493 dbus::utility::getAllProperties(
494 "xyz.openbmc_project.VirtualMedia", path,
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200495 "xyz.openbmc_project.VirtualMedia.MountPoint",
Ed Tanouse4b32752024-02-09 18:56:29 -0800496 [&conn, path](const boost::system::error_code& ec,
497 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400498 afterGetSocket(conn, path, ec, propertiesList);
499 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800500
501 // We need to wait for dbus and the websockets to hook up before data is
502 // sent/received. Tell the core to hold off messages until the sockets are
503 // up
504 conn.deferRead();
505}
506
507inline void onClose(crow::websocket::Connection& conn,
508 const std::string& reason)
509{
510 BMCWEB_LOG_DEBUG("nbd-proxy.onclose(reason = '{}')", reason);
511 auto session = sessions.find(&conn);
512 if (session == sessions.end())
513 {
514 BMCWEB_LOG_DEBUG("No session to close");
515 return;
516 }
517 // Remove reference to session in global map
518 sessions.erase(session);
519}
520
521inline void onMessage(crow::websocket::Connection& conn, std::string_view data,
522 crow::websocket::MessageType /*type*/,
523 std::function<void()>&& whenComplete)
524{
525 BMCWEB_LOG_DEBUG("nbd-proxy.onMessage(len = {})", data.size());
526
527 // Acquire proxy from sessions
528 auto session = sessions.find(&conn);
529 if (session == sessions.end() || session->second == nullptr)
530 {
531 whenComplete();
532 return;
533 }
534
535 session->second->send(data, std::move(whenComplete));
536}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800537} // namespace nbd_proxy
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600538
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800539namespace obmc_vm
540{
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600541
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800542inline void requestRoutes(App& app)
543{
544 static_assert(
Ed Tanous25b54db2024-04-17 15:40:31 -0700545 !(BMCWEB_VM_WEBSOCKET && BMCWEB_VM_NBDPROXY),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800546 "nbd proxy cannot be turned on at the same time as vm websocket.");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600547
Ed Tanous25b54db2024-04-17 15:40:31 -0700548 if constexpr (BMCWEB_VM_NBDPROXY)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800549 {
550 BMCWEB_ROUTE(app, "/nbd/<str>")
551 .privileges({{"ConfigureComponents", "ConfigureManager"}})
552 .websocket()
553 .onopen(nbd_proxy::onOpen)
554 .onclose(nbd_proxy::onClose)
555 .onmessageex(nbd_proxy::onMessage);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600556
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800557 BMCWEB_ROUTE(app, "/vm/0/0")
558 .privileges({{"ConfigureComponents", "ConfigureManager"}})
559 .websocket()
560 .onopen(nbd_proxy::onOpen)
561 .onclose(nbd_proxy::onClose)
562 .onmessageex(nbd_proxy::onMessage);
563 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700564 if constexpr (BMCWEB_VM_WEBSOCKET)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800565 {
566 BMCWEB_ROUTE(app, "/vm/0/0")
567 .privileges({{"ConfigureComponents", "ConfigureManager"}})
568 .websocket()
569 .onopen([](crow::websocket::Connection& conn) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400570 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanouscb13a392020-07-25 19:02:03 +0000571
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400572 if (session != nullptr)
573 {
574 conn.close("Session already connected");
575 return;
576 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600577
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400578 if (handler != nullptr)
579 {
580 conn.close("Handler already running");
581 return;
582 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800583
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400584 session = &conn;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800585
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400586 // media is the last digit of the endpoint /vm/0/0. A future
587 // enhancement can include supporting different endpoint values.
588 const char* media = "0";
Ed Tanousd98a2f92025-02-06 17:36:31 -0800589 handler = std::make_shared<Handler>(media, getIoContext());
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400590 handler->connect();
591 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800592 .onclose([](crow::websocket::Connection& conn,
593 const std::string& /*reason*/) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400594 if (&conn != session)
595 {
596 return;
597 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800598
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400599 session = nullptr;
600 handler->doClose();
Ed Tanous3515fd12024-12-11 23:34:32 -0800601 handler->inputBuffer.clear();
602 handler->outputBuffer.clear();
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400603 handler.reset();
604 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800605 .onmessage([](crow::websocket::Connection& conn,
606 const std::string& data, bool) {
Ed Tanous3515fd12024-12-11 23:34:32 -0800607 if (data.length() > handler->inputBuffer.capacity() -
608 handler->inputBuffer.size())
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400609 {
610 BMCWEB_LOG_ERROR("Buffer overrun when writing {} bytes",
611 data.length());
612 conn.close("Buffer overrun");
613 return;
614 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800615
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400616 size_t copied = boost::asio::buffer_copy(
Ed Tanous3515fd12024-12-11 23:34:32 -0800617 handler->inputBuffer.prepare(data.size()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400618 boost::asio::buffer(data));
Ed Tanous3515fd12024-12-11 23:34:32 -0800619 handler->inputBuffer.commit(copied);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400620 handler->doWrite();
621 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800622 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600623}
624
625} // namespace obmc_vm
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800626
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600627} // namespace crow