blob: addef79ad631f6bc221e7671931ad4bf12b16371 [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 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// NOLINTNEXTLINE(modernize-deprecated-headers)
14#include <signal.h>
15
16#include <boost/asio/buffer.hpp>
17#include <boost/asio/error.hpp>
18#include <boost/asio/io_context.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080019#include <boost/asio/local/stream_protocol.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080020#include <boost/asio/readable_pipe.hpp>
21#include <boost/asio/writable_pipe.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080022#include <boost/beast/core/buffers_to_string.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080023#include <boost/beast/core/error.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060024#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080025#include <boost/container/flat_map.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080026#include <boost/process/v2/process.hpp>
27#include <boost/process/v2/stdio.hpp>
Ed Tanousd7857202025-01-28 15:32:26 -080028#include <sdbusplus/message/native_types.hpp>
29#include <sdbusplus/unpack_properties.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060030
Ed Tanousd7857202025-01-28 15:32:26 -080031#include <cerrno>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050032#include <csignal>
Ed Tanousd7857202025-01-28 15:32:26 -080033#include <cstddef>
34#include <filesystem>
35#include <format>
36#include <functional>
37#include <memory>
38#include <string>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080039#include <string_view>
Ed Tanousd7857202025-01-28 15:32:26 -080040#include <system_error>
41#include <utility>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050042
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060043namespace crow
44{
Ed Tanous36c0f2a2024-02-09 13:50:26 -080045
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060046namespace obmc_vm
47{
48
Ed Tanouscf9e4172022-12-21 09:30:16 -080049// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060050static crow::websocket::Connection* session = nullptr;
51
52// The max network block device buffer size is 128kb plus 16bytes
53// for the message header:
54// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
Troy Lee4ee8f0b2021-08-02 11:08:26 +080055static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060056
57class Handler : public std::enable_shared_from_this<Handler>
58{
59 public:
Ed Tanous3bfa3b22024-01-31 12:18:03 -080060 Handler(const std::string& media, boost::asio::io_context& ios) :
61 pipeOut(ios), pipeIn(ios),
62 proxy(ios, "/usr/bin/nbd-proxy", {media},
63 boost::process::v2::process_stdio{
Ed Tanous3515fd12024-12-11 23:34:32 -080064 .in = pipeIn, .out = pipeOut, .err = nullptr})
Gunnar Mills1214b7e2020-06-04 10:11:30 -050065 {}
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060066
Ed Tanous0c0084a2019-10-24 15:57:51 -070067 ~Handler() = default;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060068
Ed Tanousecd6a3a2022-01-07 09:18:40 -080069 Handler(const Handler&) = delete;
70 Handler(Handler&&) = delete;
71 Handler& operator=(const Handler&) = delete;
72 Handler& operator=(Handler&&) = delete;
73
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060074 void doClose()
75 {
76 // boost::process::child::terminate uses SIGKILL, need to send SIGTERM
77 // to allow the proxy to stop nbd-client and the USB device gadget.
78 int rc = kill(proxy.id(), SIGTERM);
Ed Tanouse662eae2022-01-25 10:39:19 -080079 if (rc != 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060080 {
Ed Tanous62598e32023-07-17 17:06:25 -070081 BMCWEB_LOG_ERROR("Failed to terminate nbd-proxy: {}", errno);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060082 return;
83 }
Troy Lee36ecbf32021-08-17 18:15:28 +080084
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060085 proxy.wait();
86 }
87
88 void connect()
89 {
90 std::error_code ec;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060091 if (ec)
92 {
Ed Tanous62598e32023-07-17 17:06:25 -070093 BMCWEB_LOG_ERROR("Couldn't connect to nbd-proxy: {}", ec.message());
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060094 if (session != nullptr)
95 {
96 session->close("Error connecting to nbd-proxy");
97 }
98 return;
99 }
100 doWrite();
101 doRead();
102 }
103
104 void doWrite()
105 {
106 if (doingWrite)
107 {
Ed Tanous62598e32023-07-17 17:06:25 -0700108 BMCWEB_LOG_DEBUG("Already writing. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600109 return;
110 }
111
Ed Tanous3515fd12024-12-11 23:34:32 -0800112 if (inputBuffer.size() == 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600113 {
Ed Tanous62598e32023-07-17 17:06:25 -0700114 BMCWEB_LOG_DEBUG("inputBuffer empty. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600115 return;
116 }
117
118 doingWrite = true;
119 pipeIn.async_write_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800120 inputBuffer.data(),
Ed Tanous81c4e332023-05-18 10:30:34 -0700121 [this, self(shared_from_this())](const boost::beast::error_code& ec,
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600122 std::size_t bytesWritten) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400123 BMCWEB_LOG_DEBUG("Wrote {}bytes", bytesWritten);
124 doingWrite = false;
Ed Tanous3515fd12024-12-11 23:34:32 -0800125 inputBuffer.consume(bytesWritten);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600126
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400127 if (session == nullptr)
128 {
129 return;
130 }
131 if (ec == boost::asio::error::eof)
132 {
133 session->close("VM socket port closed");
134 return;
135 }
136 if (ec)
137 {
138 session->close("Error in writing to proxy port");
139 BMCWEB_LOG_ERROR("Error in VM socket write {}", ec);
140 return;
141 }
142 doWrite();
143 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600144 }
145
146 void doRead()
147 {
Ed Tanous3515fd12024-12-11 23:34:32 -0800148 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600149
150 pipeOut.async_read_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800151 outputBuffer.prepare(bytes),
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600152 [this, self(shared_from_this())](
153 const boost::system::error_code& ec, std::size_t bytesRead) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400154 BMCWEB_LOG_DEBUG("Read done. Read {} bytes", bytesRead);
155 if (ec)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600156 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400157 BMCWEB_LOG_ERROR("Couldn't read from VM port: {}", ec);
158 if (session != nullptr)
159 {
160 session->close("Error in connecting to VM port");
161 }
162 return;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600163 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400164 if (session == nullptr)
165 {
166 return;
167 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600168
Ed Tanous3515fd12024-12-11 23:34:32 -0800169 outputBuffer.commit(bytesRead);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400170 std::string_view payload(
Ed Tanous3515fd12024-12-11 23:34:32 -0800171 static_cast<const char*>(outputBuffer.data().data()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400172 bytesRead);
173 session->sendBinary(payload);
Ed Tanous3515fd12024-12-11 23:34:32 -0800174 outputBuffer.consume(bytesRead);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600175
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400176 doRead();
177 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600178 }
179
Ed Tanous3bfa3b22024-01-31 12:18:03 -0800180 boost::asio::readable_pipe pipeOut;
181 boost::asio::writable_pipe pipeIn;
182 boost::process::v2::process proxy;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800183 bool doingWrite{false};
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600184
Ed Tanous3515fd12024-12-11 23:34:32 -0800185 boost::beast::flat_static_buffer<nbdBufferSize> outputBuffer;
186 boost::beast::flat_static_buffer<nbdBufferSize> inputBuffer;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600187};
188
Ed Tanouscf9e4172022-12-21 09:30:16 -0800189// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600190static std::shared_ptr<Handler> handler;
191
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800192} // namespace obmc_vm
193
194namespace nbd_proxy
195{
196using boost::asio::local::stream_protocol;
197
198// The max network block device buffer size is 128kb plus 16bytes
199// for the message header:
200// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
201static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
202
203struct NbdProxyServer : std::enable_shared_from_this<NbdProxyServer>
204{
205 NbdProxyServer(crow::websocket::Connection& connIn,
206 const std::string& socketIdIn,
207 const std::string& endpointIdIn, const std::string& pathIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400208 socketId(socketIdIn), endpointId(endpointIdIn), path(pathIn),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800209
210 peerSocket(connIn.getIoContext()),
211 acceptor(connIn.getIoContext(), stream_protocol::endpoint(socketId)),
212 connection(connIn)
213 {}
214
215 NbdProxyServer(const NbdProxyServer&) = delete;
216 NbdProxyServer(NbdProxyServer&&) = delete;
217 NbdProxyServer& operator=(const NbdProxyServer&) = delete;
218 NbdProxyServer& operator=(NbdProxyServer&&) = delete;
219
220 ~NbdProxyServer()
221 {
222 BMCWEB_LOG_DEBUG("NbdProxyServer destructor");
223
224 BMCWEB_LOG_DEBUG("peerSocket->close()");
225 boost::system::error_code ec;
226 peerSocket.close(ec);
227
228 BMCWEB_LOG_DEBUG("std::filesystem::remove({})", socketId);
229 std::error_code ec2;
230 std::filesystem::remove(socketId.c_str(), ec2);
231 if (ec2)
232 {
233 BMCWEB_LOG_DEBUG("Failed to remove file, ignoring");
234 }
235
236 crow::connections::systemBus->async_method_call(
237 dbus::utility::logError, "xyz.openbmc_project.VirtualMedia", path,
238 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
239 }
240
241 std::string getEndpointId() const
242 {
243 return endpointId;
244 }
245
Ed Tanouse4b32752024-02-09 18:56:29 -0800246 static void afterMount(const std::weak_ptr<NbdProxyServer>& weak,
247 const boost::system::error_code& ec,
248 bool /*isBinary*/)
249 {
250 std::shared_ptr<NbdProxyServer> self = weak.lock();
251 if (self == nullptr)
252 {
253 return;
254 }
255 if (ec)
256 {
257 BMCWEB_LOG_ERROR("DBus error: cannot call mount method = {}",
258 ec.message());
259
260 self->connection.close("Failed to mount media");
261 return;
262 }
263 }
264
265 static void afterAccept(const std::weak_ptr<NbdProxyServer>& weak,
266 const boost::system::error_code& ec,
267 stream_protocol::socket socket)
268 {
269 if (ec)
270 {
271 BMCWEB_LOG_ERROR("UNIX socket: async_accept error = {}",
272 ec.message());
273 return;
274 }
275
276 BMCWEB_LOG_DEBUG("Connection opened");
277 std::shared_ptr<NbdProxyServer> self = weak.lock();
278 if (self == nullptr)
279 {
280 return;
281 }
282
283 self->connection.resumeRead();
284 self->peerSocket = std::move(socket);
285 // Start reading from socket
286 self->doRead();
287 }
288
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800289 void run()
290 {
291 acceptor.async_accept(
Ed Tanouse4b32752024-02-09 18:56:29 -0800292 std::bind_front(&NbdProxyServer::afterAccept, weak_from_this()));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800293
294 crow::connections::systemBus->async_method_call(
Ed Tanouse4b32752024-02-09 18:56:29 -0800295 [weak{weak_from_this()}](const boost::system::error_code& ec,
296 bool isBinary) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400297 afterMount(weak, ec, isBinary);
298 },
Ed Tanouse4b32752024-02-09 18:56:29 -0800299 "xyz.openbmc_project.VirtualMedia", path,
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800300 "xyz.openbmc_project.VirtualMedia.Proxy", "Mount");
301 }
302
303 void send(std::string_view buffer, std::function<void()>&& onDone)
304 {
305 size_t copied = boost::asio::buffer_copy(
306 ws2uxBuf.prepare(buffer.size()), boost::asio::buffer(buffer));
307 ws2uxBuf.commit(copied);
308
309 doWrite(std::move(onDone));
310 }
311
312 private:
Ed Tanouse4b32752024-02-09 18:56:29 -0800313 static void afterSendEx(const std::weak_ptr<NbdProxyServer>& weak)
314 {
315 std::shared_ptr<NbdProxyServer> self2 = weak.lock();
316 if (self2 != nullptr)
317 {
318 self2->ux2wsBuf.consume(self2->ux2wsBuf.size());
319 self2->doRead();
320 }
321 }
322
Ed Tanouse43512a2024-08-06 10:50:33 -0700323 void afterRead(const std::weak_ptr<NbdProxyServer>& weak,
324 const boost::system::error_code& ec, size_t bytesRead)
Ed Tanouse4b32752024-02-09 18:56:29 -0800325 {
326 if (ec)
327 {
328 BMCWEB_LOG_ERROR("UNIX socket: async_read_some error = {}",
329 ec.message());
330 return;
331 }
332 std::shared_ptr<NbdProxyServer> self = weak.lock();
333 if (self == nullptr)
334 {
335 return;
336 }
337
338 // Send to websocket
339 self->ux2wsBuf.commit(bytesRead);
340 self->connection.sendEx(
341 crow::websocket::MessageType::Binary,
342 boost::beast::buffers_to_string(self->ux2wsBuf.data()),
343 std::bind_front(&NbdProxyServer::afterSendEx, weak_from_this()));
344 }
345
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800346 void doRead()
347 {
348 // Trigger async read
Ed Tanouse4b32752024-02-09 18:56:29 -0800349 peerSocket.async_read_some(ux2wsBuf.prepare(nbdBufferSize),
350 std::bind_front(&NbdProxyServer::afterRead,
351 this, weak_from_this()));
352 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800353
Ed Tanouse4b32752024-02-09 18:56:29 -0800354 static void afterWrite(const std::weak_ptr<NbdProxyServer>& weak,
355 std::function<void()>&& onDone,
356 const boost::system::error_code& ec,
357 size_t bytesWritten)
358 {
359 std::shared_ptr<NbdProxyServer> self = weak.lock();
360 if (self == nullptr)
361 {
362 return;
363 }
364
365 self->ws2uxBuf.consume(bytesWritten);
366 self->uxWriteInProgress = false;
367
368 if (ec)
369 {
370 BMCWEB_LOG_ERROR("UNIX: async_write error = {}", ec.message());
371 self->connection.close("Internal error");
372 return;
373 }
374
375 // Retrigger doWrite if there is something in buffer
376 if (self->ws2uxBuf.size() > 0)
377 {
378 self->doWrite(std::move(onDone));
379 return;
380 }
381 onDone();
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800382 }
383
384 void doWrite(std::function<void()>&& onDone)
385 {
386 if (uxWriteInProgress)
387 {
388 BMCWEB_LOG_ERROR("Write in progress");
389 return;
390 }
391
392 if (ws2uxBuf.size() == 0)
393 {
394 BMCWEB_LOG_ERROR("No data to write to UNIX socket");
395 return;
396 }
397
398 uxWriteInProgress = true;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400399 peerSocket.async_write_some(
400 ws2uxBuf.data(),
401 std::bind_front(&NbdProxyServer::afterWrite, weak_from_this(),
402 std::move(onDone)));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800403 }
404
405 // Keeps UNIX socket endpoint file path
406 const std::string socketId;
407 const std::string endpointId;
408 const std::string path;
409
410 bool uxWriteInProgress = false;
411
412 // UNIX => WebSocket buffer
413 boost::beast::flat_static_buffer<nbdBufferSize> ux2wsBuf;
414
415 // WebSocket => UNIX buffer
416 boost::beast::flat_static_buffer<nbdBufferSize> ws2uxBuf;
417
418 // The socket used to communicate with the client.
419 stream_protocol::socket peerSocket;
420
421 // Default acceptor for UNIX socket
422 stream_protocol::acceptor acceptor;
423
424 crow::websocket::Connection& connection;
425};
426
427using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
428 std::shared_ptr<NbdProxyServer>>;
429// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
430static SessionMap sessions;
431
432inline void
Ed Tanouse4b32752024-02-09 18:56:29 -0800433 afterGetSocket(crow::websocket::Connection& conn,
434 const sdbusplus::message::object_path& path,
435 const boost::system::error_code& ec,
436 const dbus::utility::DBusPropertiesMap& propertiesList)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800437{
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800438 if (ec)
439 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200440 BMCWEB_LOG_ERROR("DBus getAllProperties error: {}", ec.message());
Ed Tanouse4b32752024-02-09 18:56:29 -0800441 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800442 return;
443 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800444 std::string endpointId;
445 std::string socket;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800446
Ed Tanouse4b32752024-02-09 18:56:29 -0800447 bool success = sdbusplus::unpackPropertiesNoThrow(
448 redfish::dbus_utils::UnpackErrorPrinter(), propertiesList, "EndpointId",
449 endpointId, "Socket", socket);
450
451 if (!success)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800452 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200453 BMCWEB_LOG_ERROR("Failed to unpack properties");
Ed Tanouse4b32752024-02-09 18:56:29 -0800454 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800455 return;
456 }
457
458 for (const auto& session : sessions)
459 {
460 if (session.second->getEndpointId() == conn.url().path())
461 {
462 BMCWEB_LOG_ERROR("Cannot open new connection - socket is in use");
463 conn.close("Slot is in use");
464 return;
465 }
466 }
467
468 // If the socket file exists (i.e. after bmcweb crash),
469 // we cannot reuse it.
Ed Tanous80ba22f2024-05-06 15:37:17 -0700470 std::error_code ec2;
471 std::filesystem::remove(socket.c_str(), ec2);
472 // Ignore failures. File might not exist.
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800473
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400474 sessions[&conn] =
475 std::make_shared<NbdProxyServer>(conn, socket, endpointId, path);
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800476 sessions[&conn]->run();
Ed Tanouse4b32752024-02-09 18:56:29 -0800477}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800478
479inline void onOpen(crow::websocket::Connection& conn)
480{
481 BMCWEB_LOG_DEBUG("nbd-proxy.onopen({})", logPtr(&conn));
482
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200483 if (conn.url().segments().size() < 2)
484 {
485 BMCWEB_LOG_ERROR("Invalid path - \"{}\"", conn.url().path());
486 conn.close("Internal error");
487 return;
488 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800489
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200490 std::string index = conn.url().segments().back();
491 std::string path =
492 std::format("/xyz/openbmc_project/VirtualMedia/Proxy/Slot_{}", index);
Ed Tanouse4b32752024-02-09 18:56:29 -0800493
Ed Tanousdeae6a72024-11-11 21:58:57 -0800494 dbus::utility::getAllProperties(
495 "xyz.openbmc_project.VirtualMedia", path,
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200496 "xyz.openbmc_project.VirtualMedia.MountPoint",
Ed Tanouse4b32752024-02-09 18:56:29 -0800497 [&conn, path](const boost::system::error_code& ec,
498 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400499 afterGetSocket(conn, path, ec, propertiesList);
500 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800501
502 // We need to wait for dbus and the websockets to hook up before data is
503 // sent/received. Tell the core to hold off messages until the sockets are
504 // up
505 conn.deferRead();
506}
507
508inline void onClose(crow::websocket::Connection& conn,
509 const std::string& reason)
510{
511 BMCWEB_LOG_DEBUG("nbd-proxy.onclose(reason = '{}')", reason);
512 auto session = sessions.find(&conn);
513 if (session == sessions.end())
514 {
515 BMCWEB_LOG_DEBUG("No session to close");
516 return;
517 }
518 // Remove reference to session in global map
519 sessions.erase(session);
520}
521
522inline void onMessage(crow::websocket::Connection& conn, std::string_view data,
523 crow::websocket::MessageType /*type*/,
524 std::function<void()>&& whenComplete)
525{
526 BMCWEB_LOG_DEBUG("nbd-proxy.onMessage(len = {})", data.size());
527
528 // Acquire proxy from sessions
529 auto session = sessions.find(&conn);
530 if (session == sessions.end() || session->second == nullptr)
531 {
532 whenComplete();
533 return;
534 }
535
536 session->second->send(data, std::move(whenComplete));
537}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800538} // namespace nbd_proxy
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600539
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800540namespace obmc_vm
541{
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600542
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800543inline void requestRoutes(App& app)
544{
545 static_assert(
Ed Tanous25b54db2024-04-17 15:40:31 -0700546 !(BMCWEB_VM_WEBSOCKET && BMCWEB_VM_NBDPROXY),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800547 "nbd proxy cannot be turned on at the same time as vm websocket.");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600548
Ed Tanous25b54db2024-04-17 15:40:31 -0700549 if constexpr (BMCWEB_VM_NBDPROXY)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800550 {
551 BMCWEB_ROUTE(app, "/nbd/<str>")
552 .privileges({{"ConfigureComponents", "ConfigureManager"}})
553 .websocket()
554 .onopen(nbd_proxy::onOpen)
555 .onclose(nbd_proxy::onClose)
556 .onmessageex(nbd_proxy::onMessage);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600557
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800558 BMCWEB_ROUTE(app, "/vm/0/0")
559 .privileges({{"ConfigureComponents", "ConfigureManager"}})
560 .websocket()
561 .onopen(nbd_proxy::onOpen)
562 .onclose(nbd_proxy::onClose)
563 .onmessageex(nbd_proxy::onMessage);
564 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700565 if constexpr (BMCWEB_VM_WEBSOCKET)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800566 {
567 BMCWEB_ROUTE(app, "/vm/0/0")
568 .privileges({{"ConfigureComponents", "ConfigureManager"}})
569 .websocket()
570 .onopen([](crow::websocket::Connection& conn) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400571 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanouscb13a392020-07-25 19:02:03 +0000572
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400573 if (session != nullptr)
574 {
575 conn.close("Session already connected");
576 return;
577 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600578
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400579 if (handler != nullptr)
580 {
581 conn.close("Handler already running");
582 return;
583 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800584
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400585 session = &conn;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800586
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400587 // media is the last digit of the endpoint /vm/0/0. A future
588 // enhancement can include supporting different endpoint values.
589 const char* media = "0";
590 handler = std::make_shared<Handler>(media, conn.getIoContext());
591 handler->connect();
592 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800593 .onclose([](crow::websocket::Connection& conn,
594 const std::string& /*reason*/) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400595 if (&conn != session)
596 {
597 return;
598 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800599
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400600 session = nullptr;
601 handler->doClose();
Ed Tanous3515fd12024-12-11 23:34:32 -0800602 handler->inputBuffer.clear();
603 handler->outputBuffer.clear();
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400604 handler.reset();
605 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800606 .onmessage([](crow::websocket::Connection& conn,
607 const std::string& data, bool) {
Ed Tanous3515fd12024-12-11 23:34:32 -0800608 if (data.length() > handler->inputBuffer.capacity() -
609 handler->inputBuffer.size())
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400610 {
611 BMCWEB_LOG_ERROR("Buffer overrun when writing {} bytes",
612 data.length());
613 conn.close("Buffer overrun");
614 return;
615 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800616
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400617 size_t copied = boost::asio::buffer_copy(
Ed Tanous3515fd12024-12-11 23:34:32 -0800618 handler->inputBuffer.prepare(data.size()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400619 boost::asio::buffer(data));
Ed Tanous3515fd12024-12-11 23:34:32 -0800620 handler->inputBuffer.commit(copied);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400621 handler->doWrite();
622 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800623 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600624}
625
626} // namespace obmc_vm
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800627
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600628} // namespace crow