blob: e668960a1ffff95eccdf0dee543af2eb9d1fc608 [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 Tanous3ccb3ad2023-01-13 17:40:03 -08005#include "app.hpp"
Ed Tanous36c0f2a2024-02-09 13:50:26 -08006#include "dbus_utility.hpp"
7#include "privileges.hpp"
Ed Tanousfaf100f2023-05-25 10:03:14 -07008#include "websocket.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08009
Ed Tanous36c0f2a2024-02-09 13:50:26 -080010#include <boost/asio/local/stream_protocol.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080011#include <boost/asio/readable_pipe.hpp>
12#include <boost/asio/writable_pipe.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080013#include <boost/asio/write.hpp>
14#include <boost/beast/core/buffers_to_string.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060015#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080016#include <boost/container/flat_map.hpp>
Ed Tanous3bfa3b22024-01-31 12:18:03 -080017#include <boost/process/v2/process.hpp>
18#include <boost/process/v2/stdio.hpp>
Ed Tanouse4b32752024-02-09 18:56:29 -080019#include <sdbusplus/asio/property.hpp>
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060020
Gunnar Mills1214b7e2020-06-04 10:11:30 -050021#include <csignal>
Ed Tanous36c0f2a2024-02-09 13:50:26 -080022#include <string_view>
Gunnar Mills1214b7e2020-06-04 10:11:30 -050023
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060024namespace crow
25{
Ed Tanous36c0f2a2024-02-09 13:50:26 -080026
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060027namespace obmc_vm
28{
29
Ed Tanouscf9e4172022-12-21 09:30:16 -080030// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060031static crow::websocket::Connection* session = nullptr;
32
33// The max network block device buffer size is 128kb plus 16bytes
34// for the message header:
35// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
Troy Lee4ee8f0b2021-08-02 11:08:26 +080036static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060037
38class Handler : public std::enable_shared_from_this<Handler>
39{
40 public:
Ed Tanous3bfa3b22024-01-31 12:18:03 -080041 Handler(const std::string& media, boost::asio::io_context& ios) :
42 pipeOut(ios), pipeIn(ios),
43 proxy(ios, "/usr/bin/nbd-proxy", {media},
44 boost::process::v2::process_stdio{
Ed Tanous3515fd12024-12-11 23:34:32 -080045 .in = pipeIn, .out = pipeOut, .err = nullptr})
Gunnar Mills1214b7e2020-06-04 10:11:30 -050046 {}
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060047
Ed Tanous0c0084a2019-10-24 15:57:51 -070048 ~Handler() = default;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060049
Ed Tanousecd6a3a2022-01-07 09:18:40 -080050 Handler(const Handler&) = delete;
51 Handler(Handler&&) = delete;
52 Handler& operator=(const Handler&) = delete;
53 Handler& operator=(Handler&&) = delete;
54
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060055 void doClose()
56 {
57 // boost::process::child::terminate uses SIGKILL, need to send SIGTERM
58 // to allow the proxy to stop nbd-client and the USB device gadget.
59 int rc = kill(proxy.id(), SIGTERM);
Ed Tanouse662eae2022-01-25 10:39:19 -080060 if (rc != 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060061 {
Ed Tanous62598e32023-07-17 17:06:25 -070062 BMCWEB_LOG_ERROR("Failed to terminate nbd-proxy: {}", errno);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060063 return;
64 }
Troy Lee36ecbf32021-08-17 18:15:28 +080065
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060066 proxy.wait();
67 }
68
69 void connect()
70 {
71 std::error_code ec;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060072 if (ec)
73 {
Ed Tanous62598e32023-07-17 17:06:25 -070074 BMCWEB_LOG_ERROR("Couldn't connect to nbd-proxy: {}", ec.message());
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060075 if (session != nullptr)
76 {
77 session->close("Error connecting to nbd-proxy");
78 }
79 return;
80 }
81 doWrite();
82 doRead();
83 }
84
85 void doWrite()
86 {
87 if (doingWrite)
88 {
Ed Tanous62598e32023-07-17 17:06:25 -070089 BMCWEB_LOG_DEBUG("Already writing. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060090 return;
91 }
92
Ed Tanous3515fd12024-12-11 23:34:32 -080093 if (inputBuffer.size() == 0)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060094 {
Ed Tanous62598e32023-07-17 17:06:25 -070095 BMCWEB_LOG_DEBUG("inputBuffer empty. Bailing out");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060096 return;
97 }
98
99 doingWrite = true;
100 pipeIn.async_write_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800101 inputBuffer.data(),
Ed Tanous81c4e332023-05-18 10:30:34 -0700102 [this, self(shared_from_this())](const boost::beast::error_code& ec,
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600103 std::size_t bytesWritten) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400104 BMCWEB_LOG_DEBUG("Wrote {}bytes", bytesWritten);
105 doingWrite = false;
Ed Tanous3515fd12024-12-11 23:34:32 -0800106 inputBuffer.consume(bytesWritten);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600107
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400108 if (session == nullptr)
109 {
110 return;
111 }
112 if (ec == boost::asio::error::eof)
113 {
114 session->close("VM socket port closed");
115 return;
116 }
117 if (ec)
118 {
119 session->close("Error in writing to proxy port");
120 BMCWEB_LOG_ERROR("Error in VM socket write {}", ec);
121 return;
122 }
123 doWrite();
124 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600125 }
126
127 void doRead()
128 {
Ed Tanous3515fd12024-12-11 23:34:32 -0800129 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600130
131 pipeOut.async_read_some(
Ed Tanous3515fd12024-12-11 23:34:32 -0800132 outputBuffer.prepare(bytes),
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600133 [this, self(shared_from_this())](
134 const boost::system::error_code& ec, std::size_t bytesRead) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400135 BMCWEB_LOG_DEBUG("Read done. Read {} bytes", bytesRead);
136 if (ec)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600137 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400138 BMCWEB_LOG_ERROR("Couldn't read from VM port: {}", ec);
139 if (session != nullptr)
140 {
141 session->close("Error in connecting to VM port");
142 }
143 return;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600144 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400145 if (session == nullptr)
146 {
147 return;
148 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600149
Ed Tanous3515fd12024-12-11 23:34:32 -0800150 outputBuffer.commit(bytesRead);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400151 std::string_view payload(
Ed Tanous3515fd12024-12-11 23:34:32 -0800152 static_cast<const char*>(outputBuffer.data().data()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400153 bytesRead);
154 session->sendBinary(payload);
Ed Tanous3515fd12024-12-11 23:34:32 -0800155 outputBuffer.consume(bytesRead);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600156
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400157 doRead();
158 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600159 }
160
Ed Tanous3bfa3b22024-01-31 12:18:03 -0800161 boost::asio::readable_pipe pipeOut;
162 boost::asio::writable_pipe pipeIn;
163 boost::process::v2::process proxy;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800164 bool doingWrite{false};
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600165
Ed Tanous3515fd12024-12-11 23:34:32 -0800166 boost::beast::flat_static_buffer<nbdBufferSize> outputBuffer;
167 boost::beast::flat_static_buffer<nbdBufferSize> inputBuffer;
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600168};
169
Ed Tanouscf9e4172022-12-21 09:30:16 -0800170// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600171static std::shared_ptr<Handler> handler;
172
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800173} // namespace obmc_vm
174
175namespace nbd_proxy
176{
177using boost::asio::local::stream_protocol;
178
179// The max network block device buffer size is 128kb plus 16bytes
180// for the message header:
181// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
182static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
183
184struct NbdProxyServer : std::enable_shared_from_this<NbdProxyServer>
185{
186 NbdProxyServer(crow::websocket::Connection& connIn,
187 const std::string& socketIdIn,
188 const std::string& endpointIdIn, const std::string& pathIn) :
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400189 socketId(socketIdIn), endpointId(endpointIdIn), path(pathIn),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800190
191 peerSocket(connIn.getIoContext()),
192 acceptor(connIn.getIoContext(), stream_protocol::endpoint(socketId)),
193 connection(connIn)
194 {}
195
196 NbdProxyServer(const NbdProxyServer&) = delete;
197 NbdProxyServer(NbdProxyServer&&) = delete;
198 NbdProxyServer& operator=(const NbdProxyServer&) = delete;
199 NbdProxyServer& operator=(NbdProxyServer&&) = delete;
200
201 ~NbdProxyServer()
202 {
203 BMCWEB_LOG_DEBUG("NbdProxyServer destructor");
204
205 BMCWEB_LOG_DEBUG("peerSocket->close()");
206 boost::system::error_code ec;
207 peerSocket.close(ec);
208
209 BMCWEB_LOG_DEBUG("std::filesystem::remove({})", socketId);
210 std::error_code ec2;
211 std::filesystem::remove(socketId.c_str(), ec2);
212 if (ec2)
213 {
214 BMCWEB_LOG_DEBUG("Failed to remove file, ignoring");
215 }
216
217 crow::connections::systemBus->async_method_call(
218 dbus::utility::logError, "xyz.openbmc_project.VirtualMedia", path,
219 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
220 }
221
222 std::string getEndpointId() const
223 {
224 return endpointId;
225 }
226
Ed Tanouse4b32752024-02-09 18:56:29 -0800227 static void afterMount(const std::weak_ptr<NbdProxyServer>& weak,
228 const boost::system::error_code& ec,
229 bool /*isBinary*/)
230 {
231 std::shared_ptr<NbdProxyServer> self = weak.lock();
232 if (self == nullptr)
233 {
234 return;
235 }
236 if (ec)
237 {
238 BMCWEB_LOG_ERROR("DBus error: cannot call mount method = {}",
239 ec.message());
240
241 self->connection.close("Failed to mount media");
242 return;
243 }
244 }
245
246 static void afterAccept(const std::weak_ptr<NbdProxyServer>& weak,
247 const boost::system::error_code& ec,
248 stream_protocol::socket socket)
249 {
250 if (ec)
251 {
252 BMCWEB_LOG_ERROR("UNIX socket: async_accept error = {}",
253 ec.message());
254 return;
255 }
256
257 BMCWEB_LOG_DEBUG("Connection opened");
258 std::shared_ptr<NbdProxyServer> self = weak.lock();
259 if (self == nullptr)
260 {
261 return;
262 }
263
264 self->connection.resumeRead();
265 self->peerSocket = std::move(socket);
266 // Start reading from socket
267 self->doRead();
268 }
269
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800270 void run()
271 {
272 acceptor.async_accept(
Ed Tanouse4b32752024-02-09 18:56:29 -0800273 std::bind_front(&NbdProxyServer::afterAccept, weak_from_this()));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800274
275 crow::connections::systemBus->async_method_call(
Ed Tanouse4b32752024-02-09 18:56:29 -0800276 [weak{weak_from_this()}](const boost::system::error_code& ec,
277 bool isBinary) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400278 afterMount(weak, ec, isBinary);
279 },
Ed Tanouse4b32752024-02-09 18:56:29 -0800280 "xyz.openbmc_project.VirtualMedia", path,
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800281 "xyz.openbmc_project.VirtualMedia.Proxy", "Mount");
282 }
283
284 void send(std::string_view buffer, std::function<void()>&& onDone)
285 {
286 size_t copied = boost::asio::buffer_copy(
287 ws2uxBuf.prepare(buffer.size()), boost::asio::buffer(buffer));
288 ws2uxBuf.commit(copied);
289
290 doWrite(std::move(onDone));
291 }
292
293 private:
Ed Tanouse4b32752024-02-09 18:56:29 -0800294 static void afterSendEx(const std::weak_ptr<NbdProxyServer>& weak)
295 {
296 std::shared_ptr<NbdProxyServer> self2 = weak.lock();
297 if (self2 != nullptr)
298 {
299 self2->ux2wsBuf.consume(self2->ux2wsBuf.size());
300 self2->doRead();
301 }
302 }
303
Ed Tanouse43512a2024-08-06 10:50:33 -0700304 void afterRead(const std::weak_ptr<NbdProxyServer>& weak,
305 const boost::system::error_code& ec, size_t bytesRead)
Ed Tanouse4b32752024-02-09 18:56:29 -0800306 {
307 if (ec)
308 {
309 BMCWEB_LOG_ERROR("UNIX socket: async_read_some error = {}",
310 ec.message());
311 return;
312 }
313 std::shared_ptr<NbdProxyServer> self = weak.lock();
314 if (self == nullptr)
315 {
316 return;
317 }
318
319 // Send to websocket
320 self->ux2wsBuf.commit(bytesRead);
321 self->connection.sendEx(
322 crow::websocket::MessageType::Binary,
323 boost::beast::buffers_to_string(self->ux2wsBuf.data()),
324 std::bind_front(&NbdProxyServer::afterSendEx, weak_from_this()));
325 }
326
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800327 void doRead()
328 {
329 // Trigger async read
Ed Tanouse4b32752024-02-09 18:56:29 -0800330 peerSocket.async_read_some(ux2wsBuf.prepare(nbdBufferSize),
331 std::bind_front(&NbdProxyServer::afterRead,
332 this, weak_from_this()));
333 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800334
Ed Tanouse4b32752024-02-09 18:56:29 -0800335 static void afterWrite(const std::weak_ptr<NbdProxyServer>& weak,
336 std::function<void()>&& onDone,
337 const boost::system::error_code& ec,
338 size_t bytesWritten)
339 {
340 std::shared_ptr<NbdProxyServer> self = weak.lock();
341 if (self == nullptr)
342 {
343 return;
344 }
345
346 self->ws2uxBuf.consume(bytesWritten);
347 self->uxWriteInProgress = false;
348
349 if (ec)
350 {
351 BMCWEB_LOG_ERROR("UNIX: async_write error = {}", ec.message());
352 self->connection.close("Internal error");
353 return;
354 }
355
356 // Retrigger doWrite if there is something in buffer
357 if (self->ws2uxBuf.size() > 0)
358 {
359 self->doWrite(std::move(onDone));
360 return;
361 }
362 onDone();
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800363 }
364
365 void doWrite(std::function<void()>&& onDone)
366 {
367 if (uxWriteInProgress)
368 {
369 BMCWEB_LOG_ERROR("Write in progress");
370 return;
371 }
372
373 if (ws2uxBuf.size() == 0)
374 {
375 BMCWEB_LOG_ERROR("No data to write to UNIX socket");
376 return;
377 }
378
379 uxWriteInProgress = true;
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400380 peerSocket.async_write_some(
381 ws2uxBuf.data(),
382 std::bind_front(&NbdProxyServer::afterWrite, weak_from_this(),
383 std::move(onDone)));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800384 }
385
386 // Keeps UNIX socket endpoint file path
387 const std::string socketId;
388 const std::string endpointId;
389 const std::string path;
390
391 bool uxWriteInProgress = false;
392
393 // UNIX => WebSocket buffer
394 boost::beast::flat_static_buffer<nbdBufferSize> ux2wsBuf;
395
396 // WebSocket => UNIX buffer
397 boost::beast::flat_static_buffer<nbdBufferSize> ws2uxBuf;
398
399 // The socket used to communicate with the client.
400 stream_protocol::socket peerSocket;
401
402 // Default acceptor for UNIX socket
403 stream_protocol::acceptor acceptor;
404
405 crow::websocket::Connection& connection;
406};
407
408using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
409 std::shared_ptr<NbdProxyServer>>;
410// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
411static SessionMap sessions;
412
413inline void
Ed Tanouse4b32752024-02-09 18:56:29 -0800414 afterGetSocket(crow::websocket::Connection& conn,
415 const sdbusplus::message::object_path& path,
416 const boost::system::error_code& ec,
417 const dbus::utility::DBusPropertiesMap& propertiesList)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800418{
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800419 if (ec)
420 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200421 BMCWEB_LOG_ERROR("DBus getAllProperties error: {}", ec.message());
Ed Tanouse4b32752024-02-09 18:56:29 -0800422 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800423 return;
424 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800425 std::string endpointId;
426 std::string socket;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800427
Ed Tanouse4b32752024-02-09 18:56:29 -0800428 bool success = sdbusplus::unpackPropertiesNoThrow(
429 redfish::dbus_utils::UnpackErrorPrinter(), propertiesList, "EndpointId",
430 endpointId, "Socket", socket);
431
432 if (!success)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800433 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200434 BMCWEB_LOG_ERROR("Failed to unpack properties");
Ed Tanouse4b32752024-02-09 18:56:29 -0800435 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800436 return;
437 }
438
439 for (const auto& session : sessions)
440 {
441 if (session.second->getEndpointId() == conn.url().path())
442 {
443 BMCWEB_LOG_ERROR("Cannot open new connection - socket is in use");
444 conn.close("Slot is in use");
445 return;
446 }
447 }
448
449 // If the socket file exists (i.e. after bmcweb crash),
450 // we cannot reuse it.
Ed Tanous80ba22f2024-05-06 15:37:17 -0700451 std::error_code ec2;
452 std::filesystem::remove(socket.c_str(), ec2);
453 // Ignore failures. File might not exist.
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800454
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400455 sessions[&conn] =
456 std::make_shared<NbdProxyServer>(conn, socket, endpointId, path);
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800457 sessions[&conn]->run();
Ed Tanouse4b32752024-02-09 18:56:29 -0800458}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800459
460inline void onOpen(crow::websocket::Connection& conn)
461{
462 BMCWEB_LOG_DEBUG("nbd-proxy.onopen({})", logPtr(&conn));
463
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200464 if (conn.url().segments().size() < 2)
465 {
466 BMCWEB_LOG_ERROR("Invalid path - \"{}\"", conn.url().path());
467 conn.close("Internal error");
468 return;
469 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800470
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200471 std::string index = conn.url().segments().back();
472 std::string path =
473 std::format("/xyz/openbmc_project/VirtualMedia/Proxy/Slot_{}", index);
Ed Tanouse4b32752024-02-09 18:56:29 -0800474
Ed Tanousdeae6a72024-11-11 21:58:57 -0800475 dbus::utility::getAllProperties(
476 "xyz.openbmc_project.VirtualMedia", path,
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200477 "xyz.openbmc_project.VirtualMedia.MountPoint",
Ed Tanouse4b32752024-02-09 18:56:29 -0800478 [&conn, path](const boost::system::error_code& ec,
479 const dbus::utility::DBusPropertiesMap& propertiesList) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400480 afterGetSocket(conn, path, ec, propertiesList);
481 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800482
483 // We need to wait for dbus and the websockets to hook up before data is
484 // sent/received. Tell the core to hold off messages until the sockets are
485 // up
486 conn.deferRead();
487}
488
489inline void onClose(crow::websocket::Connection& conn,
490 const std::string& reason)
491{
492 BMCWEB_LOG_DEBUG("nbd-proxy.onclose(reason = '{}')", reason);
493 auto session = sessions.find(&conn);
494 if (session == sessions.end())
495 {
496 BMCWEB_LOG_DEBUG("No session to close");
497 return;
498 }
499 // Remove reference to session in global map
500 sessions.erase(session);
501}
502
503inline void onMessage(crow::websocket::Connection& conn, std::string_view data,
504 crow::websocket::MessageType /*type*/,
505 std::function<void()>&& whenComplete)
506{
507 BMCWEB_LOG_DEBUG("nbd-proxy.onMessage(len = {})", data.size());
508
509 // Acquire proxy from sessions
510 auto session = sessions.find(&conn);
511 if (session == sessions.end() || session->second == nullptr)
512 {
513 whenComplete();
514 return;
515 }
516
517 session->second->send(data, std::move(whenComplete));
518}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800519} // namespace nbd_proxy
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600520
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800521namespace obmc_vm
522{
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600523
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800524inline void requestRoutes(App& app)
525{
526 static_assert(
Ed Tanous25b54db2024-04-17 15:40:31 -0700527 !(BMCWEB_VM_WEBSOCKET && BMCWEB_VM_NBDPROXY),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800528 "nbd proxy cannot be turned on at the same time as vm websocket.");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600529
Ed Tanous25b54db2024-04-17 15:40:31 -0700530 if constexpr (BMCWEB_VM_NBDPROXY)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800531 {
532 BMCWEB_ROUTE(app, "/nbd/<str>")
533 .privileges({{"ConfigureComponents", "ConfigureManager"}})
534 .websocket()
535 .onopen(nbd_proxy::onOpen)
536 .onclose(nbd_proxy::onClose)
537 .onmessageex(nbd_proxy::onMessage);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600538
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800539 BMCWEB_ROUTE(app, "/vm/0/0")
540 .privileges({{"ConfigureComponents", "ConfigureManager"}})
541 .websocket()
542 .onopen(nbd_proxy::onOpen)
543 .onclose(nbd_proxy::onClose)
544 .onmessageex(nbd_proxy::onMessage);
545 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700546 if constexpr (BMCWEB_VM_WEBSOCKET)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800547 {
548 BMCWEB_ROUTE(app, "/vm/0/0")
549 .privileges({{"ConfigureComponents", "ConfigureManager"}})
550 .websocket()
551 .onopen([](crow::websocket::Connection& conn) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400552 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanouscb13a392020-07-25 19:02:03 +0000553
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400554 if (session != nullptr)
555 {
556 conn.close("Session already connected");
557 return;
558 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600559
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400560 if (handler != nullptr)
561 {
562 conn.close("Handler already running");
563 return;
564 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800565
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400566 session = &conn;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800567
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400568 // media is the last digit of the endpoint /vm/0/0. A future
569 // enhancement can include supporting different endpoint values.
570 const char* media = "0";
571 handler = std::make_shared<Handler>(media, conn.getIoContext());
572 handler->connect();
573 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800574 .onclose([](crow::websocket::Connection& conn,
575 const std::string& /*reason*/) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400576 if (&conn != session)
577 {
578 return;
579 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800580
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400581 session = nullptr;
582 handler->doClose();
Ed Tanous3515fd12024-12-11 23:34:32 -0800583 handler->inputBuffer.clear();
584 handler->outputBuffer.clear();
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400585 handler.reset();
586 })
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800587 .onmessage([](crow::websocket::Connection& conn,
588 const std::string& data, bool) {
Ed Tanous3515fd12024-12-11 23:34:32 -0800589 if (data.length() > handler->inputBuffer.capacity() -
590 handler->inputBuffer.size())
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400591 {
592 BMCWEB_LOG_ERROR("Buffer overrun when writing {} bytes",
593 data.length());
594 conn.close("Buffer overrun");
595 return;
596 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800597
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400598 size_t copied = boost::asio::buffer_copy(
Ed Tanous3515fd12024-12-11 23:34:32 -0800599 handler->inputBuffer.prepare(data.size()),
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400600 boost::asio::buffer(data));
Ed Tanous3515fd12024-12-11 23:34:32 -0800601 handler->inputBuffer.commit(copied);
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400602 handler->doWrite();
603 });
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800604 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600605}
606
607} // namespace obmc_vm
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800608
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600609} // namespace crow