blob: d45d4143a4a09a16cba1b833d222477b8940296c [file] [log] [blame]
Ed Tanous3eb2f352018-12-20 12:30:45 -08001#pragma once
Ed Tanous3eb2f352018-12-20 12:30:45 -08002#include <sys/socket.h>
3
Ed Tanous04e438c2020-10-03 08:06:26 -07004#include <app.hpp>
Iwona Klimaszewskac0a1c8a2019-07-12 18:26:38 +02005#include <async_resp.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -08006#include <boost/container/flat_map.hpp>
Ed Tanous04e438c2020-10-03 08:06:26 -07007#include <websocket.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -08008
9namespace crow
10{
11namespace obmc_kvm
12{
13
Jae Hyun Yooa133b292019-09-16 10:38:47 -070014static constexpr const uint maxSessions = 4;
Ed Tanous3eb2f352018-12-20 12:30:45 -080015
Jae Hyun Yooa133b292019-09-16 10:38:47 -070016class KvmSession
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080017{
Jae Hyun Yooa133b292019-09-16 10:38:47 -070018 public:
Ed Tanous23a21a12020-07-25 04:45:05 +000019 explicit KvmSession(crow::websocket::Connection& connIn) :
Ed Tanousf5b191a2022-02-15 11:30:39 -080020 conn(connIn), hostSocket(conn.getIoContext())
Jae Hyun Yooa133b292019-09-16 10:38:47 -070021 {
22 boost::asio::ip::tcp::endpoint endpoint(
Johnathan Manteyb8c7eb22020-02-19 12:44:19 -080023 boost::asio::ip::make_address("127.0.0.1"), 5900);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070024 hostSocket.async_connect(
Ed Tanous23a21a12020-07-25 04:45:05 +000025 endpoint, [this, &connIn](const boost::system::error_code& ec) {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070026 if (ec)
27 {
28 BMCWEB_LOG_ERROR
29 << "conn:" << &conn
30 << ", Couldn't connect to KVM socket port: " << ec;
31 if (ec != boost::asio::error::operation_aborted)
32 {
Ed Tanous23a21a12020-07-25 04:45:05 +000033 connIn.close("Error in connecting to KVM port");
Jae Hyun Yooa133b292019-09-16 10:38:47 -070034 }
35 return;
36 }
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080037
Jae Hyun Yooa133b292019-09-16 10:38:47 -070038 doRead();
39 });
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080040 }
41
Jae Hyun Yooa133b292019-09-16 10:38:47 -070042 void onMessage(const std::string& data)
Ed Tanous3eb2f352018-12-20 12:30:45 -080043 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070044 if (data.length() > inputBuffer.capacity())
Ed Tanous3eb2f352018-12-20 12:30:45 -080045 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070046 BMCWEB_LOG_ERROR << "conn:" << &conn
47 << ", Buffer overrun when writing "
48 << data.length() << " bytes";
49 conn.close("Buffer overrun");
50 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -080051 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070052
53 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Read " << data.size()
54 << " bytes from websocket";
55 boost::asio::buffer_copy(inputBuffer.prepare(data.size()),
56 boost::asio::buffer(data));
Gunnar Millscaa3ce32020-07-08 14:46:53 -050057 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Committing " << data.size()
Jae Hyun Yooa133b292019-09-16 10:38:47 -070058 << " bytes from websocket";
59 inputBuffer.commit(data.size());
60
61 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", inputbuffer size "
62 << inputBuffer.size();
63 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();
70 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Reading " << bytes
71 << " from kvm socket";
72 hostSocket.async_read_some(
73 outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
74 [this](const boost::system::error_code& ec, std::size_t bytesRead) {
75 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", read done. Read "
76 << bytesRead << " bytes";
77 if (ec)
78 {
79 BMCWEB_LOG_ERROR
80 << "conn:" << &conn
81 << ", Couldn't read from KVM socket port: " << ec;
82 if (ec != boost::asio::error::operation_aborted)
83 {
84 conn.close("Error in connecting to KVM port");
85 }
86 return;
87 }
88
89 outputBuffer.commit(bytesRead);
90 std::string_view payload(
91 static_cast<const char*>(outputBuffer.data().data()),
92 bytesRead);
93 BMCWEB_LOG_DEBUG << "conn:" << &conn
94 << ", Sending payload size " << payload.size();
95 conn.sendBinary(payload);
96 outputBuffer.consume(bytesRead);
97
98 doRead();
99 });
100 }
101
102 void doWrite()
103 {
104 if (doingWrite)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800105 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700106 BMCWEB_LOG_DEBUG << "conn:" << &conn
107 << ", Already writing. Bailing out";
108 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800109 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700110 if (inputBuffer.size() == 0)
111 {
112 BMCWEB_LOG_DEBUG << "conn:" << &conn
113 << ", inputBuffer empty. Bailing out";
114 return;
115 }
116
117 doingWrite = true;
118 hostSocket.async_write_some(
119 inputBuffer.data(), [this](const boost::system::error_code& ec,
120 std::size_t bytesWritten) {
121 BMCWEB_LOG_DEBUG << "conn:" << &conn << ", Wrote "
122 << bytesWritten << "bytes";
123 doingWrite = false;
124 inputBuffer.consume(bytesWritten);
125
126 if (ec == boost::asio::error::eof)
127 {
128 conn.close("KVM socket port closed");
129 return;
130 }
131 if (ec)
132 {
133 BMCWEB_LOG_ERROR << "conn:" << &conn
134 << ", Error in KVM socket write " << ec;
135 if (ec != boost::asio::error::operation_aborted)
136 {
137 conn.close("Error in reading to host port");
138 }
139 return;
140 }
141
142 doWrite();
143 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800144 }
145
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700146 crow::websocket::Connection& conn;
147 boost::asio::ip::tcp::socket hostSocket;
Ed Tanous6de264c2022-01-06 12:47:59 -0800148 boost::beast::flat_static_buffer<1024UL * 50UL> outputBuffer;
149 boost::beast::flat_static_buffer<1024UL> inputBuffer;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800150 bool doingWrite{false};
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700151};
152
153static boost::container::flat_map<crow::websocket::Connection*,
154 std::unique_ptr<KvmSession>>
155 sessions;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800156
Ed Tanous52cc1122020-07-18 13:51:21 -0700157inline void requestRoutes(App& app)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800158{
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700159 sessions.reserve(maxSessions);
160
Ed Tanous3eb2f352018-12-20 12:30:45 -0800161 BMCWEB_ROUTE(app, "/kvm/0")
Ed Tanous432a8902021-06-14 15:28:56 -0700162 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous3eb2f352018-12-20 12:30:45 -0800163 .websocket()
Gunnar Millsccd584f2021-11-16 11:36:33 -0600164 .onopen([](crow::websocket::Connection& conn,
165 const std::shared_ptr<bmcweb::AsyncResp>&) {
Ed Tanous3eb2f352018-12-20 12:30:45 -0800166 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
167
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700168 if (sessions.size() == maxSessions)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800169 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700170 conn.close("Max sessions are already connected");
Ed Tanous3eb2f352018-12-20 12:30:45 -0800171 return;
172 }
173
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700174 sessions[&conn] = std::make_unique<KvmSession>(conn);
Ed Tanous3eb2f352018-12-20 12:30:45 -0800175 })
Ed Tanouscb13a392020-07-25 19:02:03 +0000176 .onclose([](crow::websocket::Connection& conn, const std::string&) {
177 sessions.erase(&conn);
178 })
Ed Tanous3eb2f352018-12-20 12:30:45 -0800179 .onmessage([](crow::websocket::Connection& conn,
Ed Tanouscb13a392020-07-25 19:02:03 +0000180 const std::string& data, bool) {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700181 if (sessions[&conn])
Ed Tanous3eb2f352018-12-20 12:30:45 -0800182 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700183 sessions[&conn]->onMessage(data);
Ed Tanous3eb2f352018-12-20 12:30:45 -0800184 }
Ed Tanous3eb2f352018-12-20 12:30:45 -0800185 });
186}
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700187
Ed Tanous3eb2f352018-12-20 12:30:45 -0800188} // namespace obmc_kvm
189} // namespace crow