blob: 21803293b5ae4d360df43f36dbc064eb9fb8b186 [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{
43 .in = pipeIn, .out = pipeOut, .err = nullptr}),
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -060044 outputBuffer(new boost::beast::flat_static_buffer<nbdBufferSize>),
45 inputBuffer(new boost::beast::flat_static_buffer<nbdBufferSize>)
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
93 if (inputBuffer->size() == 0)
94 {
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(
101 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) {
Ed Tanous62598e32023-07-17 17:06:25 -0700104 BMCWEB_LOG_DEBUG("Wrote {}bytes", bytesWritten);
Ed Tanous002d39b2022-05-31 08:59:27 -0700105 doingWrite = false;
106 inputBuffer->consume(bytesWritten);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600107
Ed Tanous002d39b2022-05-31 08:59:27 -0700108 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");
Ed Tanous62598e32023-07-17 17:06:25 -0700120 BMCWEB_LOG_ERROR("Error in VM socket write {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700121 return;
122 }
123 doWrite();
Patrick Williams5a39f772023-10-20 11:20:21 -0500124 });
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600125 }
126
127 void doRead()
128 {
129 std::size_t bytes = outputBuffer->capacity() - outputBuffer->size();
130
131 pipeOut.async_read_some(
132 outputBuffer->prepare(bytes),
133 [this, self(shared_from_this())](
134 const boost::system::error_code& ec, std::size_t bytesRead) {
Ed Tanous62598e32023-07-17 17:06:25 -0700135 BMCWEB_LOG_DEBUG("Read done. Read {} bytes", bytesRead);
Ed Tanous002d39b2022-05-31 08:59:27 -0700136 if (ec)
137 {
Ed Tanous62598e32023-07-17 17:06:25 -0700138 BMCWEB_LOG_ERROR("Couldn't read from VM port: {}", ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700139 if (session != nullptr)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600140 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700141 session->close("Error in connecting to VM port");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600142 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700143 return;
144 }
145 if (session == nullptr)
146 {
147 return;
148 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600149
Ed Tanous002d39b2022-05-31 08:59:27 -0700150 outputBuffer->commit(bytesRead);
151 std::string_view payload(
152 static_cast<const char*>(outputBuffer->data().data()),
153 bytesRead);
154 session->sendBinary(payload);
155 outputBuffer->consume(bytesRead);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600156
Ed Tanous002d39b2022-05-31 08:59:27 -0700157 doRead();
Patrick Williams5a39f772023-10-20 11:20:21 -0500158 });
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
166 std::unique_ptr<boost::beast::flat_static_buffer<nbdBufferSize>>
167 outputBuffer;
168 std::unique_ptr<boost::beast::flat_static_buffer<nbdBufferSize>>
169 inputBuffer;
170};
171
Ed Tanouscf9e4172022-12-21 09:30:16 -0800172// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600173static std::shared_ptr<Handler> handler;
174
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800175} // namespace obmc_vm
176
177namespace nbd_proxy
178{
179using boost::asio::local::stream_protocol;
180
181// The max network block device buffer size is 128kb plus 16bytes
182// for the message header:
183// https://github.com/NetworkBlockDevice/nbd/blob/master/doc/proto.md#simple-reply-message
184static constexpr auto nbdBufferSize = (128 * 1024 + 16) * 4;
185
186struct NbdProxyServer : std::enable_shared_from_this<NbdProxyServer>
187{
188 NbdProxyServer(crow::websocket::Connection& connIn,
189 const std::string& socketIdIn,
190 const std::string& endpointIdIn, const std::string& pathIn) :
191 socketId(socketIdIn),
192 endpointId(endpointIdIn), path(pathIn),
193
194 peerSocket(connIn.getIoContext()),
195 acceptor(connIn.getIoContext(), stream_protocol::endpoint(socketId)),
196 connection(connIn)
197 {}
198
199 NbdProxyServer(const NbdProxyServer&) = delete;
200 NbdProxyServer(NbdProxyServer&&) = delete;
201 NbdProxyServer& operator=(const NbdProxyServer&) = delete;
202 NbdProxyServer& operator=(NbdProxyServer&&) = delete;
203
204 ~NbdProxyServer()
205 {
206 BMCWEB_LOG_DEBUG("NbdProxyServer destructor");
207
208 BMCWEB_LOG_DEBUG("peerSocket->close()");
209 boost::system::error_code ec;
210 peerSocket.close(ec);
211
212 BMCWEB_LOG_DEBUG("std::filesystem::remove({})", socketId);
213 std::error_code ec2;
214 std::filesystem::remove(socketId.c_str(), ec2);
215 if (ec2)
216 {
217 BMCWEB_LOG_DEBUG("Failed to remove file, ignoring");
218 }
219
220 crow::connections::systemBus->async_method_call(
221 dbus::utility::logError, "xyz.openbmc_project.VirtualMedia", path,
222 "xyz.openbmc_project.VirtualMedia.Proxy", "Unmount");
223 }
224
225 std::string getEndpointId() const
226 {
227 return endpointId;
228 }
229
Ed Tanouse4b32752024-02-09 18:56:29 -0800230 static void afterMount(const std::weak_ptr<NbdProxyServer>& weak,
231 const boost::system::error_code& ec,
232 bool /*isBinary*/)
233 {
234 std::shared_ptr<NbdProxyServer> self = weak.lock();
235 if (self == nullptr)
236 {
237 return;
238 }
239 if (ec)
240 {
241 BMCWEB_LOG_ERROR("DBus error: cannot call mount method = {}",
242 ec.message());
243
244 self->connection.close("Failed to mount media");
245 return;
246 }
247 }
248
249 static void afterAccept(const std::weak_ptr<NbdProxyServer>& weak,
250 const boost::system::error_code& ec,
251 stream_protocol::socket socket)
252 {
253 if (ec)
254 {
255 BMCWEB_LOG_ERROR("UNIX socket: async_accept error = {}",
256 ec.message());
257 return;
258 }
259
260 BMCWEB_LOG_DEBUG("Connection opened");
261 std::shared_ptr<NbdProxyServer> self = weak.lock();
262 if (self == nullptr)
263 {
264 return;
265 }
266
267 self->connection.resumeRead();
268 self->peerSocket = std::move(socket);
269 // Start reading from socket
270 self->doRead();
271 }
272
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800273 void run()
274 {
275 acceptor.async_accept(
Ed Tanouse4b32752024-02-09 18:56:29 -0800276 std::bind_front(&NbdProxyServer::afterAccept, weak_from_this()));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800277
278 crow::connections::systemBus->async_method_call(
Ed Tanouse4b32752024-02-09 18:56:29 -0800279 [weak{weak_from_this()}](const boost::system::error_code& ec,
280 bool isBinary) {
281 afterMount(weak, ec, isBinary);
282 },
283 "xyz.openbmc_project.VirtualMedia", path,
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800284 "xyz.openbmc_project.VirtualMedia.Proxy", "Mount");
285 }
286
287 void send(std::string_view buffer, std::function<void()>&& onDone)
288 {
289 size_t copied = boost::asio::buffer_copy(
290 ws2uxBuf.prepare(buffer.size()), boost::asio::buffer(buffer));
291 ws2uxBuf.commit(copied);
292
293 doWrite(std::move(onDone));
294 }
295
296 private:
Ed Tanouse4b32752024-02-09 18:56:29 -0800297 static void afterSendEx(const std::weak_ptr<NbdProxyServer>& weak)
298 {
299 std::shared_ptr<NbdProxyServer> self2 = weak.lock();
300 if (self2 != nullptr)
301 {
302 self2->ux2wsBuf.consume(self2->ux2wsBuf.size());
303 self2->doRead();
304 }
305 }
306
Ed Tanouse43512a2024-08-06 10:50:33 -0700307 void afterRead(const std::weak_ptr<NbdProxyServer>& weak,
308 const boost::system::error_code& ec, size_t bytesRead)
Ed Tanouse4b32752024-02-09 18:56:29 -0800309 {
310 if (ec)
311 {
312 BMCWEB_LOG_ERROR("UNIX socket: async_read_some error = {}",
313 ec.message());
314 return;
315 }
316 std::shared_ptr<NbdProxyServer> self = weak.lock();
317 if (self == nullptr)
318 {
319 return;
320 }
321
322 // Send to websocket
323 self->ux2wsBuf.commit(bytesRead);
324 self->connection.sendEx(
325 crow::websocket::MessageType::Binary,
326 boost::beast::buffers_to_string(self->ux2wsBuf.data()),
327 std::bind_front(&NbdProxyServer::afterSendEx, weak_from_this()));
328 }
329
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800330 void doRead()
331 {
332 // Trigger async read
Ed Tanouse4b32752024-02-09 18:56:29 -0800333 peerSocket.async_read_some(ux2wsBuf.prepare(nbdBufferSize),
334 std::bind_front(&NbdProxyServer::afterRead,
335 this, weak_from_this()));
336 }
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800337
Ed Tanouse4b32752024-02-09 18:56:29 -0800338 static void afterWrite(const std::weak_ptr<NbdProxyServer>& weak,
339 std::function<void()>&& onDone,
340 const boost::system::error_code& ec,
341 size_t bytesWritten)
342 {
343 std::shared_ptr<NbdProxyServer> self = weak.lock();
344 if (self == nullptr)
345 {
346 return;
347 }
348
349 self->ws2uxBuf.consume(bytesWritten);
350 self->uxWriteInProgress = false;
351
352 if (ec)
353 {
354 BMCWEB_LOG_ERROR("UNIX: async_write error = {}", ec.message());
355 self->connection.close("Internal error");
356 return;
357 }
358
359 // Retrigger doWrite if there is something in buffer
360 if (self->ws2uxBuf.size() > 0)
361 {
362 self->doWrite(std::move(onDone));
363 return;
364 }
365 onDone();
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800366 }
367
368 void doWrite(std::function<void()>&& onDone)
369 {
370 if (uxWriteInProgress)
371 {
372 BMCWEB_LOG_ERROR("Write in progress");
373 return;
374 }
375
376 if (ws2uxBuf.size() == 0)
377 {
378 BMCWEB_LOG_ERROR("No data to write to UNIX socket");
379 return;
380 }
381
382 uxWriteInProgress = true;
Ed Tanouse4b32752024-02-09 18:56:29 -0800383 peerSocket.async_write_some(ws2uxBuf.data(),
384 std::bind_front(&NbdProxyServer::afterWrite,
385 weak_from_this(),
386 std::move(onDone)));
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800387 }
388
389 // Keeps UNIX socket endpoint file path
390 const std::string socketId;
391 const std::string endpointId;
392 const std::string path;
393
394 bool uxWriteInProgress = false;
395
396 // UNIX => WebSocket buffer
397 boost::beast::flat_static_buffer<nbdBufferSize> ux2wsBuf;
398
399 // WebSocket => UNIX buffer
400 boost::beast::flat_static_buffer<nbdBufferSize> ws2uxBuf;
401
402 // The socket used to communicate with the client.
403 stream_protocol::socket peerSocket;
404
405 // Default acceptor for UNIX socket
406 stream_protocol::acceptor acceptor;
407
408 crow::websocket::Connection& connection;
409};
410
411using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
412 std::shared_ptr<NbdProxyServer>>;
413// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
414static SessionMap sessions;
415
416inline void
Ed Tanouse4b32752024-02-09 18:56:29 -0800417 afterGetSocket(crow::websocket::Connection& conn,
418 const sdbusplus::message::object_path& path,
419 const boost::system::error_code& ec,
420 const dbus::utility::DBusPropertiesMap& propertiesList)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800421{
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800422 if (ec)
423 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200424 BMCWEB_LOG_ERROR("DBus getAllProperties error: {}", ec.message());
Ed Tanouse4b32752024-02-09 18:56:29 -0800425 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800426 return;
427 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800428 std::string endpointId;
429 std::string socket;
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800430
Ed Tanouse4b32752024-02-09 18:56:29 -0800431 bool success = sdbusplus::unpackPropertiesNoThrow(
432 redfish::dbus_utils::UnpackErrorPrinter(), propertiesList, "EndpointId",
433 endpointId, "Socket", socket);
434
435 if (!success)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800436 {
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200437 BMCWEB_LOG_ERROR("Failed to unpack properties");
Ed Tanouse4b32752024-02-09 18:56:29 -0800438 conn.close("Internal Error");
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800439 return;
440 }
441
442 for (const auto& session : sessions)
443 {
444 if (session.second->getEndpointId() == conn.url().path())
445 {
446 BMCWEB_LOG_ERROR("Cannot open new connection - socket is in use");
447 conn.close("Slot is in use");
448 return;
449 }
450 }
451
452 // If the socket file exists (i.e. after bmcweb crash),
453 // we cannot reuse it.
Ed Tanous80ba22f2024-05-06 15:37:17 -0700454 std::error_code ec2;
455 std::filesystem::remove(socket.c_str(), ec2);
456 // Ignore failures. File might not exist.
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800457
Ed Tanouse4b32752024-02-09 18:56:29 -0800458 sessions[&conn] = std::make_shared<NbdProxyServer>(conn, socket, endpointId,
459 path);
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800460 sessions[&conn]->run();
Ed Tanouse4b32752024-02-09 18:56:29 -0800461}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800462
463inline void onOpen(crow::websocket::Connection& conn)
464{
465 BMCWEB_LOG_DEBUG("nbd-proxy.onopen({})", logPtr(&conn));
466
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200467 if (conn.url().segments().size() < 2)
468 {
469 BMCWEB_LOG_ERROR("Invalid path - \"{}\"", conn.url().path());
470 conn.close("Internal error");
471 return;
472 }
Ed Tanouse4b32752024-02-09 18:56:29 -0800473
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200474 std::string index = conn.url().segments().back();
475 std::string path =
476 std::format("/xyz/openbmc_project/VirtualMedia/Proxy/Slot_{}", index);
Ed Tanouse4b32752024-02-09 18:56:29 -0800477
478 sdbusplus::asio::getAllProperties(
479 *crow::connections::systemBus, "xyz.openbmc_project.VirtualMedia", path,
Boleslaw Ogonczyk Makowski5ec22842024-05-16 14:23:43 +0200480 "xyz.openbmc_project.VirtualMedia.MountPoint",
Ed Tanouse4b32752024-02-09 18:56:29 -0800481 [&conn, path](const boost::system::error_code& ec,
482 const dbus::utility::DBusPropertiesMap& propertiesList) {
483 afterGetSocket(conn, path, ec, propertiesList);
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800484 });
485
486 // We need to wait for dbus and the websockets to hook up before data is
487 // sent/received. Tell the core to hold off messages until the sockets are
488 // up
489 conn.deferRead();
490}
491
492inline void onClose(crow::websocket::Connection& conn,
493 const std::string& reason)
494{
495 BMCWEB_LOG_DEBUG("nbd-proxy.onclose(reason = '{}')", reason);
496 auto session = sessions.find(&conn);
497 if (session == sessions.end())
498 {
499 BMCWEB_LOG_DEBUG("No session to close");
500 return;
501 }
502 // Remove reference to session in global map
503 sessions.erase(session);
504}
505
506inline void onMessage(crow::websocket::Connection& conn, std::string_view data,
507 crow::websocket::MessageType /*type*/,
508 std::function<void()>&& whenComplete)
509{
510 BMCWEB_LOG_DEBUG("nbd-proxy.onMessage(len = {})", data.size());
511
512 // Acquire proxy from sessions
513 auto session = sessions.find(&conn);
514 if (session == sessions.end() || session->second == nullptr)
515 {
516 whenComplete();
517 return;
518 }
519
520 session->second->send(data, std::move(whenComplete));
521}
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800522} // namespace nbd_proxy
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600523
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800524namespace obmc_vm
525{
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600526
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800527inline void requestRoutes(App& app)
528{
529 static_assert(
Ed Tanous25b54db2024-04-17 15:40:31 -0700530 !(BMCWEB_VM_WEBSOCKET && BMCWEB_VM_NBDPROXY),
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800531 "nbd proxy cannot be turned on at the same time as vm websocket.");
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600532
Ed Tanous25b54db2024-04-17 15:40:31 -0700533 if constexpr (BMCWEB_VM_NBDPROXY)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800534 {
535 BMCWEB_ROUTE(app, "/nbd/<str>")
536 .privileges({{"ConfigureComponents", "ConfigureManager"}})
537 .websocket()
538 .onopen(nbd_proxy::onOpen)
539 .onclose(nbd_proxy::onClose)
540 .onmessageex(nbd_proxy::onMessage);
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600541
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800542 BMCWEB_ROUTE(app, "/vm/0/0")
543 .privileges({{"ConfigureComponents", "ConfigureManager"}})
544 .websocket()
545 .onopen(nbd_proxy::onOpen)
546 .onclose(nbd_proxy::onClose)
547 .onmessageex(nbd_proxy::onMessage);
548 }
Ed Tanous25b54db2024-04-17 15:40:31 -0700549 if constexpr (BMCWEB_VM_WEBSOCKET)
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800550 {
551 BMCWEB_ROUTE(app, "/vm/0/0")
552 .privileges({{"ConfigureComponents", "ConfigureManager"}})
553 .websocket()
554 .onopen([](crow::websocket::Connection& conn) {
555 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanouscb13a392020-07-25 19:02:03 +0000556
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800557 if (session != nullptr)
558 {
559 conn.close("Session already connected");
560 return;
561 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600562
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800563 if (handler != nullptr)
564 {
565 conn.close("Handler already running");
566 return;
567 }
568
569 session = &conn;
570
571 // media is the last digit of the endpoint /vm/0/0. A future
572 // enhancement can include supporting different endpoint values.
573 const char* media = "0";
574 handler = std::make_shared<Handler>(media, conn.getIoContext());
575 handler->connect();
576 })
577 .onclose([](crow::websocket::Connection& conn,
578 const std::string& /*reason*/) {
579 if (&conn != session)
580 {
581 return;
582 }
583
584 session = nullptr;
585 handler->doClose();
586 handler->inputBuffer->clear();
587 handler->outputBuffer->clear();
588 handler.reset();
589 })
590 .onmessage([](crow::websocket::Connection& conn,
591 const std::string& data, bool) {
592 if (data.length() >
593 handler->inputBuffer->capacity() - handler->inputBuffer->size())
594 {
595 BMCWEB_LOG_ERROR("Buffer overrun when writing {} bytes",
596 data.length());
597 conn.close("Buffer overrun");
598 return;
599 }
600
Ed Tanous43498812024-05-15 12:39:48 -0700601 size_t copied = boost::asio::buffer_copy(
602 handler->inputBuffer->prepare(data.size()),
603 boost::asio::buffer(data));
604 handler->inputBuffer->commit(copied);
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800605 handler->doWrite();
606 });
607 }
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600608}
609
610} // namespace obmc_vm
Ed Tanous36c0f2a2024-02-09 13:50:26 -0800611
Adriana Kobylak1bfbe0e2019-01-17 12:08:38 -0600612} // namespace crow