blob: b930b640945ce4e3973f544163588640a435527b [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 Tanous36c0f2a2024-02-09 13:50:26 -08008#include "dbus_utility.hpp"
Ed Tanousd98a2f92025-02-06 17:36:31 -08009#include "io_context_singleton.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -080010#include "logging.hpp"
Ed Tanousfaf100f2023-05-25 10:03:14 -070011#include "websocket.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -080012
Ed Tanousd7857202025-01-28 15:32:26 -080013#include <boost/asio/buffer.hpp>
14#include <boost/asio/error.hpp>
15#include <boost/asio/io_context.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080016#include <boost/asio/local/stream_protocol.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080017#include <boost/asio/readable_pipe.hpp>
18#include <boost/asio/writable_pipe.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080019#include <boost/beast/core/buffers_to_string.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080020#include <boost/beast/core/error.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060021#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080022#include <boost/container/flat_map.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080023#include <boost/process/v2/process.hpp>
24#include <boost/process/v2/stdio.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080025#include <sdbusplus/message/native_types.hpp>
26#include <sdbusplus/unpack_properties.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060027
Ed Tanousd7857202025-01-28 15:32:26 -080028#include <cerrno>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050029#include <csignal>
Ed Tanousd7857202025-01-28 15:32:26 -080030#include <cstddef>
31#include <filesystem>
32#include <format>
33#include <functional>
34#include <memory>
35#include <string>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080036#include <string_view>
Ed Tanousd7857202025-01-28 15:32:26 -080037#include <system_error>
38#include <utility>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050039
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060040namespace crow
41{
Ed Tanous36c0f2a2024-02-09 13:50:26 -080042
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060043namespace obmc_vm
44{
45
Ed Tanouscf9e4172022-12-21 09:30:16 -080046// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060047static crow::websocket::Connection* session = nullptr;
48
49// The max network block device buffer size is 128kb plus 16bytes
50// for the message header:
51// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
Troy Lee4ee8f0b2021-08-02 11:08:26 +080052static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060053
54class Handler : public std::enable_shared_from_this<Handler>
55{
56 public:
Ed Tanous3bfa3b22024-01-31 12:18:03 -080057 Handler(const std::string& media, boost::asio::io_context& ios) :
58 pipeOut(ios), pipeIn(ios),
59 proxy(ios, "/usr/bin/nbd-proxy", {media},
60 boost::process::v2::process_stdio{
Ed Tanous3515fd12024-12-11 23:34:32 -080061 .in = pipeIn, .out = pipeOut, .err = nullptr})
Gunnar Mills1214b7e2020-06-04 10:11:30 -050062 {}
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060063
Ed Tanous0c0084a2019-10-24 15:57:51 -070064 ~Handler() = default;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060065
Ed Tanousecd6a3a2022-01-07 09:18:40 -080066 Handler(const Handler&) = delete;
67 Handler(Handler&&) = delete;
68 Handler& operator=(const Handler&) = delete;
69 Handler& operator=(Handler&&) = delete;
70
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060071 void doClose()
72 {
73 // boost::process::child::terminate uses SIGKILL, need to send SIGTERM
74 // to allow the proxy to stop nbd-client and the USB device gadget.
Myung Bae92e11bf2025-01-31 09:22:23 -050075 // NOLINTNEXTLINE(misc-include-cleaner)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060076 int rc = kill(proxy.id(), SIGTERM);
Ed Tanouse662eae2022-01-25 10:39:19 -080077 if (rc != 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060078 {
Ed Tanous62598e32023-07-17 17:06:25 -070079 BMCWEB_LOG_ERROR("Failed to terminate nbd-proxy: {}", errno);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060080 return;
81 }
Troy Lee36ecbf32021-08-17 18:15:28 +080082
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060083 proxy.wait();
84 }
85
86 void connect()
87 {
88 std::error_code ec;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060089 if (ec)
90 {
Ed Tanous62598e32023-07-17 17:06:25 -070091 BMCWEB_LOG_ERROR("Couldn't connect to nbd-proxy: {}", ec.message());
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060092 if (session != nullptr)
93 {
94 session->close("Error connecting to nbd-proxy");
95 }
96 return;
97 }
98 doWrite();
99 doRead();
100 }
101
102 void doWrite()
103 {
104 if (doingWrite)
105 {
Ed Tanous62598e32023-07-17 17:06:25 -0700106 BMCWEB_LOG_DEBUG("Already writing. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600107 return;
108 }
109
Ed Tanous3515fd12024-12-11 23:34:32 -0800110 if (inputBuffer.size() == 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600111 {
Ed Tanous62598e32023-07-17 17:06:25 -0700112 BMCWEB_LOG_DEBUG("inputBuffer empty. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600113 return;
114 }
115
116 doingWrite = true;
117 pipeIn.async_write_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800118 inputBuffer.data(),
Ed Tanous81c4e332023-05-18 10:30:34 -0700119 [this, self(shared_from_this())](const boost::beast::error_code& ec,
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600120 std::size_t bytesWritten) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400121 BMCWEB_LOG_DEBUG("Wrote {}bytes", bytesWritten);
122 doingWrite = false;
Ed Tanous3515fd12024-12-11 23:34:32 -0800123 inputBuffer.consume(bytesWritten);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600124
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400125 if (session == nullptr)
126 {
127 return;
128 }
129 if (ec == boost::asio::error::eof)
130 {
131 session->close("VM socket port closed");
132 return;
133 }
134 if (ec)
135 {
136 session->close("Error in writing to proxy port");
137 BMCWEB_LOG_ERROR("Error in VM socket write {}", ec);
138 return;
139 }
140 doWrite();
141 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600142 }
143
144 void doRead()
145 {
Ed Tanous3515fd12024-12-11 23:34:32 -0800146 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600147
148 pipeOut.async_read_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800149 outputBuffer.prepare(bytes),
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600150 [this, self(shared_from_this())](
151 const boost::system::error_code& ec, std::size_t bytesRead) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400152 BMCWEB_LOG_DEBUG("Read done. Read {} bytes", bytesRead);
153 if (ec)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600154 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400155 BMCWEB_LOG_ERROR("Couldn't read from VM port: {}", ec);
156 if (session != nullptr)
157 {
158 session->close("Error in connecting to VM port");
159 }
160 return;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600161 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400162 if (session == nullptr)
163 {
164 return;
165 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600166
Ed Tanous3515fd12024-12-11 23:34:32 -0800167 outputBuffer.commit(bytesRead);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400168 std::string_view payload(
Ed Tanous3515fd12024-12-11 23:34:32 -0800169 static_cast<const char*>(outputBuffer.data().data()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400170 bytesRead);
171 session->sendBinary(payload);
Ed Tanous3515fd12024-12-11 23:34:32 -0800172 outputBuffer.consume(bytesRead);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600173
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400174 doRead();
175 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600176 }
177
Ed Tanous3bfa3b22024-01-31 12:18:03 -0800178 boost::asio::readable_pipe pipeOut;
179 boost::asio::writable_pipe pipeIn;
180 boost::process::v2::process proxy;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800181 bool doingWrite{false};
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600182
Ed Tanous3515fd12024-12-11 23:34:32 -0800183 boost::beast::flat_static_buffer<nbdBufferSize> outputBuffer;
184 boost::beast::flat_static_buffer<nbdBufferSize> inputBuffer;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600185};
186
Ed Tanouscf9e4172022-12-21 09:30:16 -0800187// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600188static std::shared_ptr<Handler> handler;
189
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800190} // namespace obmc_vm
191
192namespace nbd_proxy
193{
194using boost::asio::local::stream_protocol;
195
196// The max network block device buffer size is 128kb plus 16bytes
197// for the message header:
198// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
199static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
200
201struct NbdProxyServer : std::enable_shared_from_this<NbdProxyServer>
202{
203 NbdProxyServer(crow::websocket::Connection& connIn,
204 const std::string& socketIdIn,
205 const std::string& endpointIdIn, const std::string& pathIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400206 socketId(socketIdIn), endpointId(endpointIdIn), path(pathIn),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800207
Ed Tanousd98a2f92025-02-06 17:36:31 -0800208 peerSocket(getIoContext()),
209 acceptor(getIoContext(), stream_protocol::endpoint(socketId)),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800210 connection(connIn)
211 {}
212
213 NbdProxyServer(const NbdProxyServer&) = delete;
214 NbdProxyServer(NbdProxyServer&&) = delete;
215 NbdProxyServer& operator=(const NbdProxyServer&) = delete;
216 NbdProxyServer& operator=(NbdProxyServer&&) = delete;
217
218 ~NbdProxyServer()
219 {
220 BMCWEB_LOG_DEBUG("NbdProxyServer destructor");
221
222 BMCWEB_LOG_DEBUG("peerSocket->close()");
223 boost::system::error_code ec;
224 peerSocket.close(ec);
225
226 BMCWEB_LOG_DEBUG("std::filesystem::remove({})", socketId);
227 std::error_code ec2;
228 std::filesystem::remove(socketId.c_str(), ec2);
229 if (ec2)
230 {
231 BMCWEB_LOG_DEBUG("Failed to remove file, ignoring");
232 }
233
Ed Tanous177612a2025-02-14 15:16:09 -0800234 dbus::utility::async_method_call(
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800235 dbus::utility::logError, "xyz.openbmc_project.VirtualMedia", path,
236 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
237 }
238
239 std::string getEndpointId() const
240 {
241 return endpointId;
242 }
243
Ed Tanouse4b32752024-02-09 18:56:29 -0800244 static void afterMount(const std::weak_ptr<NbdProxyServer>& weak,
245 const boost::system::error_code& ec,
246 bool /*isBinary*/)
247 {
248 std::shared_ptr<NbdProxyServer> self = weak.lock();
249 if (self == nullptr)
250 {
251 return;
252 }
253 if (ec)
254 {
255 BMCWEB_LOG_ERROR("DBus error: cannot call mount method = {}",
256 ec.message());
257
258 self->connection.close("Failed to mount media");
259 return;
260 }
261 }
262
263 static void afterAccept(const std::weak_ptr<NbdProxyServer>& weak,
264 const boost::system::error_code& ec,
265 stream_protocol::socket socket)
266 {
267 if (ec)
268 {
269 BMCWEB_LOG_ERROR("UNIX socket: async_accept error = {}",
270 ec.message());
271 return;
272 }
273
274 BMCWEB_LOG_DEBUG("Connection opened");
275 std::shared_ptr<NbdProxyServer> self = weak.lock();
276 if (self == nullptr)
277 {
278 return;
279 }
280
281 self->connection.resumeRead();
282 self->peerSocket = std::move(socket);
283 // Start reading from socket
284 self->doRead();
285 }
286
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800287 void run()
288 {
289 acceptor.async_accept(
Ed Tanouse4b32752024-02-09 18:56:29 -0800290 std::bind_front(&NbdProxyServer::afterAccept, weak_from_this()));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800291
Ed Tanous177612a2025-02-14 15:16:09 -0800292 dbus::utility::async_method_call(
Ed Tanouse4b32752024-02-09 18:56:29 -0800293 [weak{weak_from_this()}](const boost::system::error_code& ec,
294 bool isBinary) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400295 afterMount(weak, ec, isBinary);
296 },
Ed Tanouse4b32752024-02-09 18:56:29 -0800297 "xyz.openbmc_project.VirtualMedia", path,
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800298 "xyz.openbmc_project.VirtualMedia.Proxy", "Mount");
299 }
300
301 void send(std::string_view buffer, std::function<void()>&& onDone)
302 {
303 size_t copied = boost::asio::buffer_copy(
304 ws2uxBuf.prepare(buffer.size()), boost::asio::buffer(buffer));
305 ws2uxBuf.commit(copied);
306
307 doWrite(std::move(onDone));
308 }
309
310 private:
Ed Tanouse4b32752024-02-09 18:56:29 -0800311 static void afterSendEx(const std::weak_ptr<NbdProxyServer>& weak)
312 {
313 std::shared_ptr<NbdProxyServer> self2 = weak.lock();
314 if (self2 != nullptr)
315 {
316 self2->ux2wsBuf.consume(self2->ux2wsBuf.size());
317 self2->doRead();
318 }
319 }
320
Ed Tanouse43512a2024-08-06 10:50:33 -0700321 void afterRead(const std::weak_ptr<NbdProxyServer>& weak,
322 const boost::system::error_code& ec, size_t bytesRead)
Ed Tanouse4b32752024-02-09 18:56:29 -0800323 {
324 if (ec)
325 {
326 BMCWEB_LOG_ERROR("UNIX socket: async_read_some error = {}",
327 ec.message());
328 return;
329 }
330 std::shared_ptr<NbdProxyServer> self = weak.lock();
331 if (self == nullptr)
332 {
333 return;
334 }
335
336 // Send to websocket
337 self->ux2wsBuf.commit(bytesRead);
338 self->connection.sendEx(
339 crow::websocket::MessageType::Binary,
340 boost::beast::buffers_to_string(self->ux2wsBuf.data()),
341 std::bind_front(&NbdProxyServer::afterSendEx, weak_from_this()));
342 }
343
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800344 void doRead()
345 {
346 // Trigger async read
Ed Tanouse4b32752024-02-09 18:56:29 -0800347 peerSocket.async_read_some(ux2wsBuf.prepare(nbdBufferSize),
348 std::bind_front(&NbdProxyServer::afterRead,
349 this, weak_from_this()));
350 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800351
Ed Tanouse4b32752024-02-09 18:56:29 -0800352 static void afterWrite(const std::weak_ptr<NbdProxyServer>& weak,
353 std::function<void()>&& onDone,
354 const boost::system::error_code& ec,
355 size_t bytesWritten)
356 {
357 std::shared_ptr<NbdProxyServer> self = weak.lock();
358 if (self == nullptr)
359 {
360 return;
361 }
362
363 self->ws2uxBuf.consume(bytesWritten);
364 self->uxWriteInProgress = false;
365
366 if (ec)
367 {
368 BMCWEB_LOG_ERROR("UNIX: async_write error = {}", ec.message());
369 self->connection.close("Internal error");
370 return;
371 }
372
373 // Retrigger doWrite if there is something in buffer
374 if (self->ws2uxBuf.size() > 0)
375 {
376 self->doWrite(std::move(onDone));
377 return;
378 }
379 onDone();
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800380 }
381
382 void doWrite(std::function<void()>&& onDone)
383 {
384 if (uxWriteInProgress)
385 {
386 BMCWEB_LOG_ERROR("Write in progress");
387 return;
388 }
389
390 if (ws2uxBuf.size() == 0)
391 {
392 BMCWEB_LOG_ERROR("No data to write to UNIX socket");
393 return;
394 }
395
396 uxWriteInProgress = true;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400397 peerSocket.async_write_some(
398 ws2uxBuf.data(),
399 std::bind_front(&NbdProxyServer::afterWrite, weak_from_this(),
400 std::move(onDone)));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800401 }
402
403 // Keeps UNIX socket endpoint file path
404 const std::string socketId;
405 const std::string endpointId;
406 const std::string path;
407
408 bool uxWriteInProgress = false;
409
410 // UNIX => WebSocket buffer
411 boost::beast::flat_static_buffer<nbdBufferSize> ux2wsBuf;
412
413 // WebSocket => UNIX buffer
414 boost::beast::flat_static_buffer<nbdBufferSize> ws2uxBuf;
415
416 // The socket used to communicate with the client.
417 stream_protocol::socket peerSocket;
418
419 // Default acceptor for UNIX socket
420 stream_protocol::acceptor acceptor;
421
422 crow::websocket::Connection& connection;
423};
424
425using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
426 std::shared_ptr<NbdProxyServer>>;
427// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
428static SessionMap sessions;
429
Patrick Williams504af5a2025-02-03 14:29:03 -0500430inline void afterGetSocket(
431 crow::websocket::Connection& conn,
432 const sdbusplus::message::object_path& path,
433 const boost::system::error_code& ec,
434 const dbus::utility::DBusPropertiesMap& propertiesList)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800435{
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800436 if (ec)
437 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200438 BMCWEB_LOG_ERROR("DBus getAllProperties error: {}", ec.message());
Ed Tanouse4b32752024-02-09 18:56:29 -0800439 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800440 return;
441 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800442 std::string endpointId;
443 std::string socket;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800444
Ed Tanouse4b32752024-02-09 18:56:29 -0800445 bool success = sdbusplus::unpackPropertiesNoThrow(
446 redfish::dbus_utils::UnpackErrorPrinter(), propertiesList, "EndpointId",
447 endpointId, "Socket", socket);
448
449 if (!success)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800450 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200451 BMCWEB_LOG_ERROR("Failed to unpack properties");
Ed Tanouse4b32752024-02-09 18:56:29 -0800452 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800453 return;
454 }
455
456 for (const auto& session : sessions)
457 {
458 if (session.second->getEndpointId() == conn.url().path())
459 {
460 BMCWEB_LOG_ERROR("Cannot open new connection - socket is in use");
461 conn.close("Slot is in use");
462 return;
463 }
464 }
465
466 // If the socket file exists (i.e. after bmcweb crash),
467 // we cannot reuse it.
Ed Tanous80ba22f2024-05-06 15:37:17 -0700468 std::error_code ec2;
469 std::filesystem::remove(socket.c_str(), ec2);
470 // Ignore failures. File might not exist.
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800471
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400472 sessions[&conn] =
473 std::make_shared<NbdProxyServer>(conn, socket, endpointId, path);
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800474 sessions[&conn]->run();
Ed Tanouse4b32752024-02-09 18:56:29 -0800475}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800476
477inline void onOpen(crow::websocket::Connection& conn)
478{
479 BMCWEB_LOG_DEBUG("nbd-proxy.onopen({})", logPtr(&conn));
480
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200481 if (conn.url().segments().size() < 2)
482 {
483 BMCWEB_LOG_ERROR("Invalid path - \"{}\"", conn.url().path());
484 conn.close("Internal error");
485 return;
486 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800487
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200488 std::string index = conn.url().segments().back();
489 std::string path =
490 std::format("/xyz/openbmc_project/VirtualMedia/Proxy/Slot_{}", index);
Ed Tanouse4b32752024-02-09 18:56:29 -0800491
Ed Tanousdeae6a72024-11-11 21:58:57 -0800492 dbus::utility::getAllProperties(
493 "xyz.openbmc_project.VirtualMedia", path,
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200494 "xyz.openbmc_project.VirtualMedia.MountPoint",
Ed Tanouse4b32752024-02-09 18:56:29 -0800495 [&conn, path](const boost::system::error_code& ec,
496 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400497 afterGetSocket(conn, path, ec, propertiesList);
498 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800499
500 // We need to wait for dbus and the websockets to hook up before data is
501 // sent/received. Tell the core to hold off messages until the sockets are
502 // up
503 conn.deferRead();
504}
505
506inline void onClose(crow::websocket::Connection& conn,
507 const std::string& reason)
508{
509 BMCWEB_LOG_DEBUG("nbd-proxy.onclose(reason = '{}')", reason);
510 auto session = sessions.find(&conn);
511 if (session == sessions.end())
512 {
513 BMCWEB_LOG_DEBUG("No session to close");
514 return;
515 }
516 // Remove reference to session in global map
517 sessions.erase(session);
518}
519
520inline void onMessage(crow::websocket::Connection& conn, std::string_view data,
521 crow::websocket::MessageType /*type*/,
522 std::function<void()>&& whenComplete)
523{
524 BMCWEB_LOG_DEBUG("nbd-proxy.onMessage(len = {})", data.size());
525
526 // Acquire proxy from sessions
527 auto session = sessions.find(&conn);
528 if (session == sessions.end() || session->second == nullptr)
529 {
530 whenComplete();
531 return;
532 }
533
534 session->second->send(data, std::move(whenComplete));
535}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800536} // namespace nbd_proxy
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600537
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800538namespace obmc_vm
539{
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600540
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800541inline void requestRoutes(App& app)
542{
543 static_assert(
Ed Tanous25b54db2024-04-17 15:40:31 -0700544 !(BMCWEB_VM_WEBSOCKET && BMCWEB_VM_NBDPROXY),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800545 "nbd proxy cannot be turned on at the same time as vm websocket.");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600546
Ed Tanous25b54db2024-04-17 15:40:31 -0700547 if constexpr (BMCWEB_VM_NBDPROXY)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800548 {
549 BMCWEB_ROUTE(app, "/nbd/<str>")
550 .privileges({{"ConfigureComponents", "ConfigureManager"}})
551 .websocket()
552 .onopen(nbd_proxy::onOpen)
553 .onclose(nbd_proxy::onClose)
554 .onmessageex(nbd_proxy::onMessage);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600555
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800556 BMCWEB_ROUTE(app, "/vm/0/0")
557 .privileges({{"ConfigureComponents", "ConfigureManager"}})
558 .websocket()
559 .onopen(nbd_proxy::onOpen)
560 .onclose(nbd_proxy::onClose)
561 .onmessageex(nbd_proxy::onMessage);
562 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700563 if constexpr (BMCWEB_VM_WEBSOCKET)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800564 {
565 BMCWEB_ROUTE(app, "/vm/0/0")
566 .privileges({{"ConfigureComponents", "ConfigureManager"}})
567 .websocket()
568 .onopen([](crow::websocket::Connection& conn) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400569 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanouscb13a392020-07-25 19:02:03 +0000570
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400571 if (session != nullptr)
572 {
573 conn.close("Session already connected");
574 return;
575 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600576
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400577 if (handler != nullptr)
578 {
579 conn.close("Handler already running");
580 return;
581 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800582
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400583 session = &conn;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800584
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400585 // media is the last digit of the endpoint /vm/0/0. A future
586 // enhancement can include supporting different endpoint values.
587 const char* media = "0";
Ed Tanousd98a2f92025-02-06 17:36:31 -0800588 handler = std::make_shared<Handler>(media, getIoContext());
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400589 handler->connect();
590 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800591 .onclose([](crow::websocket::Connection& conn,
592 const std::string& /*reason*/) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400593 if (&conn != session)
594 {
595 return;
596 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800597
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400598 session = nullptr;
599 handler->doClose();
Ed Tanous3515fd12024-12-11 23:34:32 -0800600 handler->inputBuffer.clear();
601 handler->outputBuffer.clear();
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400602 handler.reset();
603 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800604 .onmessage([](crow::websocket::Connection& conn,
605 const std::string& data, bool) {
Ed Tanous3515fd12024-12-11 23:34:32 -0800606 if (data.length() > handler->inputBuffer.capacity() -
607 handler->inputBuffer.size())
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400608 {
609 BMCWEB_LOG_ERROR("Buffer overrun when writing {} bytes",
610 data.length());
611 conn.close("Buffer overrun");
612 return;
613 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800614
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400615 size_t copied = boost::asio::buffer_copy(
Ed Tanous3515fd12024-12-11 23:34:32 -0800616 handler->inputBuffer.prepare(data.size()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400617 boost::asio::buffer(data));
Ed Tanous3515fd12024-12-11 23:34:32 -0800618 handler->inputBuffer.commit(copied);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400619 handler->doWrite();
620 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800621 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600622}
623
624} // namespace obmc_vm
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800625
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600626} // namespace crow