blob: df13a674612b24a03812ba64b0579a3773eb573b [file] [log] [blame]
Ed Tanous3eb2f352018-12-20 12:30:45 -08001#pragma once
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08002#include "app.hpp"
3#include "async_resp.hpp"
Ed Tanousfaf100f2023-05-25 10:03:14 -07004#include "websocket.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08005
Ed Tanous3eb2f352018-12-20 12:30:45 -08006#include <sys/socket.h>
7
8#include <boost/container/flat_map.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -08009
10namespace crow
11{
12namespace obmc_kvm
13{
14
Jae Hyun Yooa133b292019-09-16 10:38:47 -070015static constexpr const uint maxSessions = 4;
Ed Tanous3eb2f352018-12-20 12:30:45 -080016
Xinnan Xie8e73b902023-08-23 11:14:48 +080017class KvmSession : public std::enable_shared_from_this<KvmSession>
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080018{
Jae Hyun Yooa133b292019-09-16 10:38:47 -070019 public:
Ed Tanous23a21a12020-07-25 04:45:05 +000020 explicit KvmSession(crow::websocket::Connection& connIn) :
Ed Tanousf5b191a2022-02-15 11:30:39 -080021 conn(connIn), hostSocket(conn.getIoContext())
Jae Hyun Yooa133b292019-09-16 10:38:47 -070022 {
23 boost::asio::ip::tcp::endpoint endpoint(
Johnathan Manteyb8c7eb22020-02-19 12:44:19 -080024 boost::asio::ip::make_address("127.0.0.1"), 5900);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070025 hostSocket.async_connect(
Ed Tanous23a21a12020-07-25 04:45:05 +000026 endpoint, [this, &connIn](const boost::system::error_code& ec) {
Patrick Williams5a39f772023-10-20 11:20:21 -050027 if (ec)
28 {
29 BMCWEB_LOG_ERROR(
30 "conn:{}, Couldn't connect to KVM socket port: {}",
31 logPtr(&conn), ec);
32 if (ec != boost::asio::error::operation_aborted)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070033 {
Patrick Williams5a39f772023-10-20 11:20:21 -050034 connIn.close("Error in connecting to KVM port");
Jae Hyun Yooa133b292019-09-16 10:38:47 -070035 }
Patrick Williams5a39f772023-10-20 11:20:21 -050036 return;
37 }
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080038
Patrick Williams5a39f772023-10-20 11:20:21 -050039 doRead();
40 });
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080041 }
42
Jae Hyun Yooa133b292019-09-16 10:38:47 -070043 void onMessage(const std::string& data)
Ed Tanous3eb2f352018-12-20 12:30:45 -080044 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070045 if (data.length() > inputBuffer.capacity())
Ed Tanous3eb2f352018-12-20 12:30:45 -080046 {
Ed Tanous62598e32023-07-17 17:06:25 -070047 BMCWEB_LOG_ERROR("conn:{}, Buffer overrun when writing {} bytes",
48 logPtr(&conn), data.length());
Jae Hyun Yooa133b292019-09-16 10:38:47 -070049 conn.close("Buffer overrun");
50 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -080051 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070052
Ed Tanous62598e32023-07-17 17:06:25 -070053 BMCWEB_LOG_DEBUG("conn:{}, Read {} bytes from websocket", logPtr(&conn),
54 data.size());
Ed Tanous44106f32024-04-06 13:48:50 -070055 size_t copied = boost::asio::buffer_copy(
56 inputBuffer.prepare(data.size()), boost::asio::buffer(data));
Ed Tanous62598e32023-07-17 17:06:25 -070057 BMCWEB_LOG_DEBUG("conn:{}, Committing {} bytes from websocket",
Ed Tanous44106f32024-04-06 13:48:50 -070058 logPtr(&conn), copied);
59 inputBuffer.commit(copied);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070060
Ed Tanous62598e32023-07-17 17:06:25 -070061 BMCWEB_LOG_DEBUG("conn:{}, inputbuffer size {}", logPtr(&conn),
62 inputBuffer.size());
Jae Hyun Yooa133b292019-09-16 10:38:47 -070063 doWrite();
Ed Tanous3eb2f352018-12-20 12:30:45 -080064 }
65
Jae Hyun Yooa133b292019-09-16 10:38:47 -070066 protected:
67 void doRead()
Ed Tanous3eb2f352018-12-20 12:30:45 -080068 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070069 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Ed Tanous62598e32023-07-17 17:06:25 -070070 BMCWEB_LOG_DEBUG("conn:{}, Reading {} from kvm socket", logPtr(&conn),
71 bytes);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070072 hostSocket.async_read_some(
73 outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
Xinnan Xie8e73b902023-08-23 11:14:48 +080074 [this, weak(weak_from_this())](const boost::system::error_code& ec,
75 std::size_t bytesRead) {
76 auto self = weak.lock();
77 if (self == nullptr)
78 {
79 return;
80 }
Ed Tanous62598e32023-07-17 17:06:25 -070081 BMCWEB_LOG_DEBUG("conn:{}, read done. Read {} bytes",
82 logPtr(&conn), bytesRead);
Ed Tanous002d39b2022-05-31 08:59:27 -070083 if (ec)
84 {
Ed Tanous62598e32023-07-17 17:06:25 -070085 BMCWEB_LOG_ERROR(
86 "conn:{}, Couldn't read from KVM socket port: {}",
87 logPtr(&conn), ec);
Ed Tanous002d39b2022-05-31 08:59:27 -070088 if (ec != boost::asio::error::operation_aborted)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070089 {
Ed Tanous002d39b2022-05-31 08:59:27 -070090 conn.close("Error in connecting to KVM port");
Jae Hyun Yooa133b292019-09-16 10:38:47 -070091 }
Ed Tanous002d39b2022-05-31 08:59:27 -070092 return;
93 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070094
Ed Tanous002d39b2022-05-31 08:59:27 -070095 outputBuffer.commit(bytesRead);
96 std::string_view payload(
97 static_cast<const char*>(outputBuffer.data().data()),
98 bytesRead);
Ed Tanous62598e32023-07-17 17:06:25 -070099 BMCWEB_LOG_DEBUG("conn:{}, Sending payload size {}", logPtr(&conn),
100 payload.size());
Ed Tanous002d39b2022-05-31 08:59:27 -0700101 conn.sendBinary(payload);
102 outputBuffer.consume(bytesRead);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700103
Ed Tanous002d39b2022-05-31 08:59:27 -0700104 doRead();
Patrick Williams5a39f772023-10-20 11:20:21 -0500105 });
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700106 }
107
108 void doWrite()
109 {
110 if (doingWrite)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800111 {
Ed Tanous62598e32023-07-17 17:06:25 -0700112 BMCWEB_LOG_DEBUG("conn:{}, Already writing. Bailing out",
113 logPtr(&conn));
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700114 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800115 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700116 if (inputBuffer.size() == 0)
117 {
Ed Tanous62598e32023-07-17 17:06:25 -0700118 BMCWEB_LOG_DEBUG("conn:{}, inputBuffer empty. Bailing out",
119 logPtr(&conn));
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700120 return;
121 }
122
123 doingWrite = true;
Xinnan Xie8e73b902023-08-23 11:14:48 +0800124 hostSocket.async_write_some(
125 inputBuffer.data(),
126 [this, weak(weak_from_this())](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700127 std::size_t bytesWritten) {
Xinnan Xie8e73b902023-08-23 11:14:48 +0800128 auto self = weak.lock();
129 if (self == nullptr)
130 {
131 return;
132 }
Ed Tanous62598e32023-07-17 17:06:25 -0700133 BMCWEB_LOG_DEBUG("conn:{}, Wrote {}bytes", logPtr(&conn),
134 bytesWritten);
Ed Tanous002d39b2022-05-31 08:59:27 -0700135 doingWrite = false;
136 inputBuffer.consume(bytesWritten);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700137
Ed Tanous002d39b2022-05-31 08:59:27 -0700138 if (ec == boost::asio::error::eof)
139 {
140 conn.close("KVM socket port closed");
141 return;
142 }
143 if (ec)
144 {
Ed Tanous62598e32023-07-17 17:06:25 -0700145 BMCWEB_LOG_ERROR("conn:{}, Error in KVM socket write {}",
146 logPtr(&conn), ec);
Ed Tanous002d39b2022-05-31 08:59:27 -0700147 if (ec != boost::asio::error::operation_aborted)
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700148 {
Ed Tanous002d39b2022-05-31 08:59:27 -0700149 conn.close("Error in reading to host port");
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700150 }
Ed Tanous002d39b2022-05-31 08:59:27 -0700151 return;
152 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700153
Ed Tanous002d39b2022-05-31 08:59:27 -0700154 doWrite();
Patrick Williams5a39f772023-10-20 11:20:21 -0500155 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800156 }
157
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700158 crow::websocket::Connection& conn;
159 boost::asio::ip::tcp::socket hostSocket;
Ed Tanous6de264c2022-01-06 12:47:59 -0800160 boost::beast::flat_static_buffer<1024UL * 50UL> outputBuffer;
161 boost::beast::flat_static_buffer<1024UL> inputBuffer;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800162 bool doingWrite{false};
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700163};
164
Ed Tanouscf9e4172022-12-21 09:30:16 -0800165using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
Xinnan Xie8e73b902023-08-23 11:14:48 +0800166 std::shared_ptr<KvmSession>>;
Ed Tanouscf9e4172022-12-21 09:30:16 -0800167// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
168static SessionMap sessions;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800169
Ed Tanous52cc1122020-07-18 13:51:21 -0700170inline void requestRoutes(App& app)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800171{
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700172 sessions.reserve(maxSessions);
173
Ed Tanous3eb2f352018-12-20 12:30:45 -0800174 BMCWEB_ROUTE(app, "/kvm/0")
Ed Tanous432a8902021-06-14 15:28:56 -0700175 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous3eb2f352018-12-20 12:30:45 -0800176 .websocket()
zhanghch0577726382021-10-21 14:07:57 +0800177 .onopen([](crow::websocket::Connection& conn) {
Patrick Williams5a39f772023-10-20 11:20:21 -0500178 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanous3eb2f352018-12-20 12:30:45 -0800179
Patrick Williams5a39f772023-10-20 11:20:21 -0500180 if (sessions.size() == maxSessions)
181 {
182 conn.close("Max sessions are already connected");
183 return;
184 }
Ed Tanous3eb2f352018-12-20 12:30:45 -0800185
Patrick Williams5a39f772023-10-20 11:20:21 -0500186 sessions[&conn] = std::make_shared<KvmSession>(conn);
187 })
Ed Tanouscb13a392020-07-25 19:02:03 +0000188 .onclose([](crow::websocket::Connection& conn, const std::string&) {
Patrick Williams5a39f772023-10-20 11:20:21 -0500189 sessions.erase(&conn);
190 })
Ed Tanous3eb2f352018-12-20 12:30:45 -0800191 .onmessage([](crow::websocket::Connection& conn,
Ed Tanouscb13a392020-07-25 19:02:03 +0000192 const std::string& data, bool) {
Patrick Williams5a39f772023-10-20 11:20:21 -0500193 if (sessions[&conn])
194 {
195 sessions[&conn]->onMessage(data);
196 }
197 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800198}
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700199
Ed Tanous3eb2f352018-12-20 12:30:45 -0800200} // namespace obmc_kvm
201} // namespace crow