blob: 56a0d9a5964e7f7a922e851fb576ea8cbd28b987 [file] [log] [blame]
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -06001#pragma once
2
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08003#include "app.hpp"
Ed Tanous36c0f2a2024-02-09 13:50:26 -08004#include "dbus_utility.hpp"
5#include "privileges.hpp"
Ed Tanousfaf100f2023-05-25 10:03:14 -07006#include "websocket.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007
Ed Tanous36c0f2a2024-02-09 13:50:26 -08008#include <boost/asio/local/stream_protocol.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -08009#include <boost/asio/readable_pipe.hpp>
10#include <boost/asio/writable_pipe.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080011#include <boost/asio/write.hpp>
12#include <boost/beast/core/buffers_to_string.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060013#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080014#include <boost/container/flat_map.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080015#include <boost/process/v2/process.hpp>
16#include <boost/process/v2/stdio.hpp>
Ed Tanouse4b32752024-02-09 18:56:29 -080017#include <sdbusplus/asio/property.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060018
Gunnar Mills1214b7e2020-06-04 10:11:30 -050019#include <csignal>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080020#include <string_view>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050021
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060022namespace crow
23{
Ed Tanous36c0f2a2024-02-09 13:50:26 -080024
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060025namespace obmc_vm
26{
27
Ed Tanouscf9e4172022-12-21 09:30:16 -080028// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060029static crow::websocket::Connection* session = nullptr;
30
31// The max network block device buffer size is 128kb plus 16bytes
32// for the message header:
33// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
Troy Lee4ee8f0b2021-08-02 11:08:26 +080034static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060035
36class Handler : public std::enable_shared_from_this<Handler>
37{
38 public:
Ed Tanous3bfa3b22024-01-31 12:18:03 -080039 Handler(const std::string& media, boost::asio::io_context& ios) :
40 pipeOut(ios), pipeIn(ios),
41 proxy(ios, "/usr/bin/nbd-proxy", {media},
42 boost::process::v2::process_stdio{
Ed Tanous3515fd12024-12-11 23:34:32 -080043 .in = pipeIn, .out = pipeOut, .err = nullptr})
Gunnar Mills1214b7e2020-06-04 10:11:30 -050044 {}
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060045
Ed Tanous0c0084a2019-10-24 15:57:51 -070046 ~Handler() = default;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060047
Ed Tanousecd6a3a2022-01-07 09:18:40 -080048 Handler(const Handler&) = delete;
49 Handler(Handler&&) = delete;
50 Handler& operator=(const Handler&) = delete;
51 Handler& operator=(Handler&&) = delete;
52
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060053 void doClose()
54 {
55 // boost::process::child::terminate uses SIGKILL, need to send SIGTERM
56 // to allow the proxy to stop nbd-client and the USB device gadget.
57 int rc = kill(proxy.id(), SIGTERM);
Ed Tanouse662eae2022-01-25 10:39:19 -080058 if (rc != 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060059 {
Ed Tanous62598e32023-07-17 17:06:25 -070060 BMCWEB_LOG_ERROR("Failed to terminate nbd-proxy: {}", errno);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060061 return;
62 }
Troy Lee36ecbf32021-08-17 18:15:28 +080063
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060064 proxy.wait();
65 }
66
67 void connect()
68 {
69 std::error_code ec;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060070 if (ec)
71 {
Ed Tanous62598e32023-07-17 17:06:25 -070072 BMCWEB_LOG_ERROR("Couldn't connect to nbd-proxy: {}", ec.message());
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060073 if (session != nullptr)
74 {
75 session->close("Error connecting to nbd-proxy");
76 }
77 return;
78 }
79 doWrite();
80 doRead();
81 }
82
83 void doWrite()
84 {
85 if (doingWrite)
86 {
Ed Tanous62598e32023-07-17 17:06:25 -070087 BMCWEB_LOG_DEBUG("Already writing. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060088 return;
89 }
90
Ed Tanous3515fd12024-12-11 23:34:32 -080091 if (inputBuffer.size() == 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060092 {
Ed Tanous62598e32023-07-17 17:06:25 -070093 BMCWEB_LOG_DEBUG("inputBuffer empty. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060094 return;
95 }
96
97 doingWrite = true;
98 pipeIn.async_write_some(
Ed Tanous3515fd12024-12-11 23:34:32 -080099 inputBuffer.data(),
Ed Tanous81c4e332023-05-18 10:30:34 -0700100 [this, self(shared_from_this())](const boost::beast::error_code& ec,
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600101 std::size_t bytesWritten) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400102 BMCWEB_LOG_DEBUG("Wrote {}bytes", bytesWritten);
103 doingWrite = false;
Ed Tanous3515fd12024-12-11 23:34:32 -0800104 inputBuffer.consume(bytesWritten);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600105
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400106 if (session == nullptr)
107 {
108 return;
109 }
110 if (ec == boost::asio::error::eof)
111 {
112 session->close("VM socket port closed");
113 return;
114 }
115 if (ec)
116 {
117 session->close("Error in writing to proxy port");
118 BMCWEB_LOG_ERROR("Error in VM socket write {}", ec);
119 return;
120 }
121 doWrite();
122 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600123 }
124
125 void doRead()
126 {
Ed Tanous3515fd12024-12-11 23:34:32 -0800127 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600128
129 pipeOut.async_read_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800130 outputBuffer.prepare(bytes),
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600131 [this, self(shared_from_this())](
132 const boost::system::error_code& ec, std::size_t bytesRead) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400133 BMCWEB_LOG_DEBUG("Read done. Read {} bytes", bytesRead);
134 if (ec)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600135 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400136 BMCWEB_LOG_ERROR("Couldn't read from VM port: {}", ec);
137 if (session != nullptr)
138 {
139 session->close("Error in connecting to VM port");
140 }
141 return;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600142 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400143 if (session == nullptr)
144 {
145 return;
146 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600147
Ed Tanous3515fd12024-12-11 23:34:32 -0800148 outputBuffer.commit(bytesRead);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400149 std::string_view payload(
Ed Tanous3515fd12024-12-11 23:34:32 -0800150 static_cast<const char*>(outputBuffer.data().data()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400151 bytesRead);
152 session->sendBinary(payload);
Ed Tanous3515fd12024-12-11 23:34:32 -0800153 outputBuffer.consume(bytesRead);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600154
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400155 doRead();
156 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600157 }
158
Ed Tanous3bfa3b22024-01-31 12:18:03 -0800159 boost::asio::readable_pipe pipeOut;
160 boost::asio::writable_pipe pipeIn;
161 boost::process::v2::process proxy;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800162 bool doingWrite{false};
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600163
Ed Tanous3515fd12024-12-11 23:34:32 -0800164 boost::beast::flat_static_buffer<nbdBufferSize> outputBuffer;
165 boost::beast::flat_static_buffer<nbdBufferSize> inputBuffer;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600166};
167
Ed Tanouscf9e4172022-12-21 09:30:16 -0800168// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600169static std::shared_ptr<Handler> handler;
170
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800171} // namespace obmc_vm
172
173namespace nbd_proxy
174{
175using boost::asio::local::stream_protocol;
176
177// The max network block device buffer size is 128kb plus 16bytes
178// for the message header:
179// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
180static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
181
182struct NbdProxyServer : std::enable_shared_from_this<NbdProxyServer>
183{
184 NbdProxyServer(crow::websocket::Connection& connIn,
185 const std::string& socketIdIn,
186 const std::string& endpointIdIn, const std::string& pathIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400187 socketId(socketIdIn), endpointId(endpointIdIn), path(pathIn),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800188
189 peerSocket(connIn.getIoContext()),
190 acceptor(connIn.getIoContext(), stream_protocol::endpoint(socketId)),
191 connection(connIn)
192 {}
193
194 NbdProxyServer(const NbdProxyServer&) = delete;
195 NbdProxyServer(NbdProxyServer&&) = delete;
196 NbdProxyServer& operator=(const NbdProxyServer&) = delete;
197 NbdProxyServer& operator=(NbdProxyServer&&) = delete;
198
199 ~NbdProxyServer()
200 {
201 BMCWEB_LOG_DEBUG("NbdProxyServer destructor");
202
203 BMCWEB_LOG_DEBUG("peerSocket->close()");
204 boost::system::error_code ec;
205 peerSocket.close(ec);
206
207 BMCWEB_LOG_DEBUG("std::filesystem::remove({})", socketId);
208 std::error_code ec2;
209 std::filesystem::remove(socketId.c_str(), ec2);
210 if (ec2)
211 {
212 BMCWEB_LOG_DEBUG("Failed to remove file, ignoring");
213 }
214
215 crow::connections::systemBus->async_method_call(
216 dbus::utility::logError, "xyz.openbmc_project.VirtualMedia", path,
217 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
218 }
219
220 std::string getEndpointId() const
221 {
222 return endpointId;
223 }
224
Ed Tanouse4b32752024-02-09 18:56:29 -0800225 static void afterMount(const std::weak_ptr<NbdProxyServer>& weak,
226 const boost::system::error_code& ec,
227 bool /*isBinary*/)
228 {
229 std::shared_ptr<NbdProxyServer> self = weak.lock();
230 if (self == nullptr)
231 {
232 return;
233 }
234 if (ec)
235 {
236 BMCWEB_LOG_ERROR("DBus error: cannot call mount method = {}",
237 ec.message());
238
239 self->connection.close("Failed to mount media");
240 return;
241 }
242 }
243
244 static void afterAccept(const std::weak_ptr<NbdProxyServer>& weak,
245 const boost::system::error_code& ec,
246 stream_protocol::socket socket)
247 {
248 if (ec)
249 {
250 BMCWEB_LOG_ERROR("UNIX socket: async_accept error = {}",
251 ec.message());
252 return;
253 }
254
255 BMCWEB_LOG_DEBUG("Connection opened");
256 std::shared_ptr<NbdProxyServer> self = weak.lock();
257 if (self == nullptr)
258 {
259 return;
260 }
261
262 self->connection.resumeRead();
263 self->peerSocket = std::move(socket);
264 // Start reading from socket
265 self->doRead();
266 }
267
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800268 void run()
269 {
270 acceptor.async_accept(
Ed Tanouse4b32752024-02-09 18:56:29 -0800271 std::bind_front(&NbdProxyServer::afterAccept, weak_from_this()));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800272
273 crow::connections::systemBus->async_method_call(
Ed Tanouse4b32752024-02-09 18:56:29 -0800274 [weak{weak_from_this()}](const boost::system::error_code& ec,
275 bool isBinary) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400276 afterMount(weak, ec, isBinary);
277 },
Ed Tanouse4b32752024-02-09 18:56:29 -0800278 "xyz.openbmc_project.VirtualMedia", path,
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800279 "xyz.openbmc_project.VirtualMedia.Proxy", "Mount");
280 }
281
282 void send(std::string_view buffer, std::function<void()>&& onDone)
283 {
284 size_t copied = boost::asio::buffer_copy(
285 ws2uxBuf.prepare(buffer.size()), boost::asio::buffer(buffer));
286 ws2uxBuf.commit(copied);
287
288 doWrite(std::move(onDone));
289 }
290
291 private:
Ed Tanouse4b32752024-02-09 18:56:29 -0800292 static void afterSendEx(const std::weak_ptr<NbdProxyServer>& weak)
293 {
294 std::shared_ptr<NbdProxyServer> self2 = weak.lock();
295 if (self2 != nullptr)
296 {
297 self2->ux2wsBuf.consume(self2->ux2wsBuf.size());
298 self2->doRead();
299 }
300 }
301
Ed Tanouse43512a2024-08-06 10:50:33 -0700302 void afterRead(const std::weak_ptr<NbdProxyServer>& weak,
303 const boost::system::error_code& ec, size_t bytesRead)
Ed Tanouse4b32752024-02-09 18:56:29 -0800304 {
305 if (ec)
306 {
307 BMCWEB_LOG_ERROR("UNIX socket: async_read_some error = {}",
308 ec.message());
309 return;
310 }
311 std::shared_ptr<NbdProxyServer> self = weak.lock();
312 if (self == nullptr)
313 {
314 return;
315 }
316
317 // Send to websocket
318 self->ux2wsBuf.commit(bytesRead);
319 self->connection.sendEx(
320 crow::websocket::MessageType::Binary,
321 boost::beast::buffers_to_string(self->ux2wsBuf.data()),
322 std::bind_front(&NbdProxyServer::afterSendEx, weak_from_this()));
323 }
324
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800325 void doRead()
326 {
327 // Trigger async read
Ed Tanouse4b32752024-02-09 18:56:29 -0800328 peerSocket.async_read_some(ux2wsBuf.prepare(nbdBufferSize),
329 std::bind_front(&NbdProxyServer::afterRead,
330 this, weak_from_this()));
331 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800332
Ed Tanouse4b32752024-02-09 18:56:29 -0800333 static void afterWrite(const std::weak_ptr<NbdProxyServer>& weak,
334 std::function<void()>&& onDone,
335 const boost::system::error_code& ec,
336 size_t bytesWritten)
337 {
338 std::shared_ptr<NbdProxyServer> self = weak.lock();
339 if (self == nullptr)
340 {
341 return;
342 }
343
344 self->ws2uxBuf.consume(bytesWritten);
345 self->uxWriteInProgress = false;
346
347 if (ec)
348 {
349 BMCWEB_LOG_ERROR("UNIX: async_write error = {}", ec.message());
350 self->connection.close("Internal error");
351 return;
352 }
353
354 // Retrigger doWrite if there is something in buffer
355 if (self->ws2uxBuf.size() > 0)
356 {
357 self->doWrite(std::move(onDone));
358 return;
359 }
360 onDone();
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800361 }
362
363 void doWrite(std::function<void()>&& onDone)
364 {
365 if (uxWriteInProgress)
366 {
367 BMCWEB_LOG_ERROR("Write in progress");
368 return;
369 }
370
371 if (ws2uxBuf.size() == 0)
372 {
373 BMCWEB_LOG_ERROR("No data to write to UNIX socket");
374 return;
375 }
376
377 uxWriteInProgress = true;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400378 peerSocket.async_write_some(
379 ws2uxBuf.data(),
380 std::bind_front(&NbdProxyServer::afterWrite, weak_from_this(),
381 std::move(onDone)));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800382 }
383
384 // Keeps UNIX socket endpoint file path
385 const std::string socketId;
386 const std::string endpointId;
387 const std::string path;
388
389 bool uxWriteInProgress = false;
390
391 // UNIX => WebSocket buffer
392 boost::beast::flat_static_buffer<nbdBufferSize> ux2wsBuf;
393
394 // WebSocket => UNIX buffer
395 boost::beast::flat_static_buffer<nbdBufferSize> ws2uxBuf;
396
397 // The socket used to communicate with the client.
398 stream_protocol::socket peerSocket;
399
400 // Default acceptor for UNIX socket
401 stream_protocol::acceptor acceptor;
402
403 crow::websocket::Connection& connection;
404};
405
406using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
407 std::shared_ptr<NbdProxyServer>>;
408// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
409static SessionMap sessions;
410
411inline void
Ed Tanouse4b32752024-02-09 18:56:29 -0800412 afterGetSocket(crow::websocket::Connection& conn,
413 const sdbusplus::message::object_path& path,
414 const boost::system::error_code& ec,
415 const dbus::utility::DBusPropertiesMap& propertiesList)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800416{
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800417 if (ec)
418 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200419 BMCWEB_LOG_ERROR("DBus getAllProperties error: {}", ec.message());
Ed Tanouse4b32752024-02-09 18:56:29 -0800420 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800421 return;
422 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800423 std::string endpointId;
424 std::string socket;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800425
Ed Tanouse4b32752024-02-09 18:56:29 -0800426 bool success = sdbusplus::unpackPropertiesNoThrow(
427 redfish::dbus_utils::UnpackErrorPrinter(), propertiesList, "EndpointId",
428 endpointId, "Socket", socket);
429
430 if (!success)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800431 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200432 BMCWEB_LOG_ERROR("Failed to unpack properties");
Ed Tanouse4b32752024-02-09 18:56:29 -0800433 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800434 return;
435 }
436
437 for (const auto& session : sessions)
438 {
439 if (session.second->getEndpointId() == conn.url().path())
440 {
441 BMCWEB_LOG_ERROR("Cannot open new connection - socket is in use");
442 conn.close("Slot is in use");
443 return;
444 }
445 }
446
447 // If the socket file exists (i.e. after bmcweb crash),
448 // we cannot reuse it.
Ed Tanous80ba22f2024-05-06 15:37:17 -0700449 std::error_code ec2;
450 std::filesystem::remove(socket.c_str(), ec2);
451 // Ignore failures. File might not exist.
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800452
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400453 sessions[&conn] =
454 std::make_shared<NbdProxyServer>(conn, socket, endpointId, path);
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800455 sessions[&conn]->run();
Ed Tanouse4b32752024-02-09 18:56:29 -0800456}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800457
458inline void onOpen(crow::websocket::Connection& conn)
459{
460 BMCWEB_LOG_DEBUG("nbd-proxy.onopen({})", logPtr(&conn));
461
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200462 if (conn.url().segments().size() < 2)
463 {
464 BMCWEB_LOG_ERROR("Invalid path - \"{}\"", conn.url().path());
465 conn.close("Internal error");
466 return;
467 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800468
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200469 std::string index = conn.url().segments().back();
470 std::string path =
471 std::format("/xyz/openbmc_project/VirtualMedia/Proxy/Slot_{}", index);
Ed Tanouse4b32752024-02-09 18:56:29 -0800472
Ed Tanousdeae6a72024-11-11 21:58:57 -0800473 dbus::utility::getAllProperties(
474 "xyz.openbmc_project.VirtualMedia", path,
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200475 "xyz.openbmc_project.VirtualMedia.MountPoint",
Ed Tanouse4b32752024-02-09 18:56:29 -0800476 [&conn, path](const boost::system::error_code& ec,
477 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400478 afterGetSocket(conn, path, ec, propertiesList);
479 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800480
481 // We need to wait for dbus and the websockets to hook up before data is
482 // sent/received. Tell the core to hold off messages until the sockets are
483 // up
484 conn.deferRead();
485}
486
487inline void onClose(crow::websocket::Connection& conn,
488 const std::string& reason)
489{
490 BMCWEB_LOG_DEBUG("nbd-proxy.onclose(reason = '{}')", reason);
491 auto session = sessions.find(&conn);
492 if (session == sessions.end())
493 {
494 BMCWEB_LOG_DEBUG("No session to close");
495 return;
496 }
497 // Remove reference to session in global map
498 sessions.erase(session);
499}
500
501inline void onMessage(crow::websocket::Connection& conn, std::string_view data,
502 crow::websocket::MessageType /*type*/,
503 std::function<void()>&& whenComplete)
504{
505 BMCWEB_LOG_DEBUG("nbd-proxy.onMessage(len = {})", data.size());
506
507 // Acquire proxy from sessions
508 auto session = sessions.find(&conn);
509 if (session == sessions.end() || session->second == nullptr)
510 {
511 whenComplete();
512 return;
513 }
514
515 session->second->send(data, std::move(whenComplete));
516}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800517} // namespace nbd_proxy
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600518
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800519namespace obmc_vm
520{
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600521
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800522inline void requestRoutes(App& app)
523{
524 static_assert(
Ed Tanous25b54db2024-04-17 15:40:31 -0700525 !(BMCWEB_VM_WEBSOCKET && BMCWEB_VM_NBDPROXY),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800526 "nbd proxy cannot be turned on at the same time as vm websocket.");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600527
Ed Tanous25b54db2024-04-17 15:40:31 -0700528 if constexpr (BMCWEB_VM_NBDPROXY)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800529 {
530 BMCWEB_ROUTE(app, "/nbd/<str>")
531 .privileges({{"ConfigureComponents", "ConfigureManager"}})
532 .websocket()
533 .onopen(nbd_proxy::onOpen)
534 .onclose(nbd_proxy::onClose)
535 .onmessageex(nbd_proxy::onMessage);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600536
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800537 BMCWEB_ROUTE(app, "/vm/0/0")
538 .privileges({{"ConfigureComponents", "ConfigureManager"}})
539 .websocket()
540 .onopen(nbd_proxy::onOpen)
541 .onclose(nbd_proxy::onClose)
542 .onmessageex(nbd_proxy::onMessage);
543 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700544 if constexpr (BMCWEB_VM_WEBSOCKET)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800545 {
546 BMCWEB_ROUTE(app, "/vm/0/0")
547 .privileges({{"ConfigureComponents", "ConfigureManager"}})
548 .websocket()
549 .onopen([](crow::websocket::Connection& conn) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400550 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanouscb13a392020-07-25 19:02:03 +0000551
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400552 if (session != nullptr)
553 {
554 conn.close("Session already connected");
555 return;
556 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600557
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400558 if (handler != nullptr)
559 {
560 conn.close("Handler already running");
561 return;
562 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800563
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400564 session = &conn;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800565
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400566 // media is the last digit of the endpoint /vm/0/0. A future
567 // enhancement can include supporting different endpoint values.
568 const char* media = "0";
569 handler = std::make_shared<Handler>(media, conn.getIoContext());
570 handler->connect();
571 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800572 .onclose([](crow::websocket::Connection& conn,
573 const std::string& /*reason*/) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400574 if (&conn != session)
575 {
576 return;
577 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800578
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400579 session = nullptr;
580 handler->doClose();
Ed Tanous3515fd12024-12-11 23:34:32 -0800581 handler->inputBuffer.clear();
582 handler->outputBuffer.clear();
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400583 handler.reset();
584 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800585 .onmessage([](crow::websocket::Connection& conn,
586 const std::string& data, bool) {
Ed Tanous3515fd12024-12-11 23:34:32 -0800587 if (data.length() > handler->inputBuffer.capacity() -
588 handler->inputBuffer.size())
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400589 {
590 BMCWEB_LOG_ERROR("Buffer overrun when writing {} bytes",
591 data.length());
592 conn.close("Buffer overrun");
593 return;
594 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800595
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400596 size_t copied = boost::asio::buffer_copy(
Ed Tanous3515fd12024-12-11 23:34:32 -0800597 handler->inputBuffer.prepare(data.size()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400598 boost::asio::buffer(data));
Ed Tanous3515fd12024-12-11 23:34:32 -0800599 handler->inputBuffer.commit(copied);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400600 handler->doWrite();
601 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800602 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600603}
604
605} // namespace obmc_vm
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800606
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600607} // namespace crow