blob: 3e0e09ec3707da276c96bc67f8c965a79c26fb23 [file] [log] [blame]
Ed Tanous40e9b922024-09-10 13:50:16 -07001// SPDX-License-Identifier: Apache-2.0
2// SPDX-FileCopyrightText: Copyright OpenBMC Authors
Ed Tanous3eb2f352018-12-20 12:30:45 -08003#pragma once
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08004#include "app.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08005#include "logging.hpp"
Ed Tanousfaf100f2023-05-25 10:03:14 -07006#include "websocket.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007
Ed Tanousd7857202025-01-28 15:32:26 -08008#include <sys/types.h>
Ed Tanous3eb2f352018-12-20 12:30:45 -08009
Ed Tanousd7857202025-01-28 15:32:26 -080010#include <boost/asio/buffer.hpp>
11#include <boost/asio/error.hpp>
12#include <boost/asio/ip/address.hpp>
13#include <boost/asio/ip/tcp.hpp>
14#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -080015#include <boost/container/flat_map.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -080016
Ed Tanousd7857202025-01-28 15:32:26 -080017#include <cstddef>
18#include <memory>
19#include <string>
20
Ed Tanous3eb2f352018-12-20 12:30:45 -080021namespace crow
22{
23namespace obmc_kvm
24{
25
Jae Hyun Yooa133b292019-09-16 10:38:47 -070026static constexpr const uint maxSessions = 4;
Ed Tanous3eb2f352018-12-20 12:30:45 -080027
Xinnan Xie8e73b902023-08-23 11:14:48 +080028class KvmSession : public std::enable_shared_from_this<KvmSession>
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080029{
Jae Hyun Yooa133b292019-09-16 10:38:47 -070030 public:
Ed Tanous23a21a12020-07-25 04:45:05 +000031 explicit KvmSession(crow::websocket::Connection& connIn) :
Ed Tanousf5b191a2022-02-15 11:30:39 -080032 conn(connIn), hostSocket(conn.getIoContext())
Jae Hyun Yooa133b292019-09-16 10:38:47 -070033 {
34 boost::asio::ip::tcp::endpoint endpoint(
Johnathan Manteyb8c7eb22020-02-19 12:44:19 -080035 boost::asio::ip::make_address("127.0.0.1"), 5900);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070036 hostSocket.async_connect(
Ed Tanous23a21a12020-07-25 04:45:05 +000037 endpoint, [this, &connIn](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040038 if (ec)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070039 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040040 BMCWEB_LOG_ERROR(
41 "conn:{}, Couldn't connect to KVM socket port: {}",
42 logPtr(&conn), ec);
43 if (ec != boost::asio::error::operation_aborted)
44 {
45 connIn.close("Error in connecting to KVM port");
46 }
47 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -070048 }
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080049
Patrick Williamsbd79bce2024-08-16 15:22:20 -040050 doRead();
51 });
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080052 }
53
Jae Hyun Yooa133b292019-09-16 10:38:47 -070054 void onMessage(const std::string& data)
Ed Tanous3eb2f352018-12-20 12:30:45 -080055 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070056 if (data.length() > inputBuffer.capacity())
Ed Tanous3eb2f352018-12-20 12:30:45 -080057 {
Ed Tanous62598e32023-07-17 17:06:25 -070058 BMCWEB_LOG_ERROR("conn:{}, Buffer overrun when writing {} bytes",
59 logPtr(&conn), data.length());
Jae Hyun Yooa133b292019-09-16 10:38:47 -070060 conn.close("Buffer overrun");
61 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -080062 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070063
Ed Tanous62598e32023-07-17 17:06:25 -070064 BMCWEB_LOG_DEBUG("conn:{}, Read {} bytes from websocket", logPtr(&conn),
65 data.size());
Ed Tanous44106f32024-04-06 13:48:50 -070066 size_t copied = boost::asio::buffer_copy(
67 inputBuffer.prepare(data.size()), boost::asio::buffer(data));
Ed Tanous62598e32023-07-17 17:06:25 -070068 BMCWEB_LOG_DEBUG("conn:{}, Committing {} bytes from websocket",
Ed Tanous44106f32024-04-06 13:48:50 -070069 logPtr(&conn), copied);
70 inputBuffer.commit(copied);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070071
Ed Tanous62598e32023-07-17 17:06:25 -070072 BMCWEB_LOG_DEBUG("conn:{}, inputbuffer size {}", logPtr(&conn),
73 inputBuffer.size());
Jae Hyun Yooa133b292019-09-16 10:38:47 -070074 doWrite();
Ed Tanous3eb2f352018-12-20 12:30:45 -080075 }
76
Jae Hyun Yooa133b292019-09-16 10:38:47 -070077 protected:
78 void doRead()
Ed Tanous3eb2f352018-12-20 12:30:45 -080079 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070080 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Ed Tanous62598e32023-07-17 17:06:25 -070081 BMCWEB_LOG_DEBUG("conn:{}, Reading {} from kvm socket", logPtr(&conn),
82 bytes);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070083 hostSocket.async_read_some(
84 outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
Xinnan Xie8e73b902023-08-23 11:14:48 +080085 [this, weak(weak_from_this())](const boost::system::error_code& ec,
86 std::size_t bytesRead) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040087 auto self = weak.lock();
88 if (self == nullptr)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070089 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040090 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -070091 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -040092 BMCWEB_LOG_DEBUG("conn:{}, read done. Read {} bytes",
93 logPtr(&conn), bytesRead);
94 if (ec)
95 {
96 BMCWEB_LOG_ERROR(
97 "conn:{}, Couldn't read from KVM socket port: {}",
98 logPtr(&conn), ec);
99 if (ec != boost::asio::error::operation_aborted)
100 {
101 conn.close("Error in connecting to KVM port");
102 }
103 return;
104 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700105
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400106 outputBuffer.commit(bytesRead);
107 std::string_view payload(
108 static_cast<const char*>(outputBuffer.data().data()),
109 bytesRead);
110 BMCWEB_LOG_DEBUG("conn:{}, Sending payload size {}",
111 logPtr(&conn), payload.size());
112 conn.sendBinary(payload);
113 outputBuffer.consume(bytesRead);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700114
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400115 doRead();
116 });
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700117 }
118
119 void doWrite()
120 {
121 if (doingWrite)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800122 {
Ed Tanous62598e32023-07-17 17:06:25 -0700123 BMCWEB_LOG_DEBUG("conn:{}, Already writing. Bailing out",
124 logPtr(&conn));
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700125 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800126 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700127 if (inputBuffer.size() == 0)
128 {
Ed Tanous62598e32023-07-17 17:06:25 -0700129 BMCWEB_LOG_DEBUG("conn:{}, inputBuffer empty. Bailing out",
130 logPtr(&conn));
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700131 return;
132 }
133
134 doingWrite = true;
Xinnan Xie8e73b902023-08-23 11:14:48 +0800135 hostSocket.async_write_some(
136 inputBuffer.data(),
137 [this, weak(weak_from_this())](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700138 std::size_t bytesWritten) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400139 auto self = weak.lock();
140 if (self == nullptr)
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700141 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400142 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700143 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400144 BMCWEB_LOG_DEBUG("conn:{}, Wrote {}bytes", logPtr(&conn),
145 bytesWritten);
146 doingWrite = false;
147 inputBuffer.consume(bytesWritten);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700148
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400149 if (ec == boost::asio::error::eof)
150 {
151 conn.close("KVM socket port closed");
152 return;
153 }
154 if (ec)
155 {
156 BMCWEB_LOG_ERROR("conn:{}, Error in KVM socket write {}",
157 logPtr(&conn), ec);
158 if (ec != boost::asio::error::operation_aborted)
159 {
160 conn.close("Error in reading to host port");
161 }
162 return;
163 }
164
165 doWrite();
166 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800167 }
168
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700169 crow::websocket::Connection& conn;
170 boost::asio::ip::tcp::socket hostSocket;
Ed Tanous6de264c2022-01-06 12:47:59 -0800171 boost::beast::flat_static_buffer<1024UL * 50UL> outputBuffer;
172 boost::beast::flat_static_buffer<1024UL> inputBuffer;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800173 bool doingWrite{false};
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700174};
175
Ed Tanouscf9e4172022-12-21 09:30:16 -0800176using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
Xinnan Xie8e73b902023-08-23 11:14:48 +0800177 std::shared_ptr<KvmSession>>;
Ed Tanouscf9e4172022-12-21 09:30:16 -0800178// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
179static SessionMap sessions;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800180
Ed Tanous52cc1122020-07-18 13:51:21 -0700181inline void requestRoutes(App& app)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800182{
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700183 sessions.reserve(maxSessions);
184
Ed Tanous3eb2f352018-12-20 12:30:45 -0800185 BMCWEB_ROUTE(app, "/kvm/0")
Ed Tanous432a8902021-06-14 15:28:56 -0700186 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous3eb2f352018-12-20 12:30:45 -0800187 .websocket()
zhanghch0577726382021-10-21 14:07:57 +0800188 .onopen([](crow::websocket::Connection& conn) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400189 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanous3eb2f352018-12-20 12:30:45 -0800190
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400191 if (sessions.size() == maxSessions)
192 {
193 conn.close("Max sessions are already connected");
194 return;
195 }
Ed Tanous3eb2f352018-12-20 12:30:45 -0800196
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400197 sessions[&conn] = std::make_shared<KvmSession>(conn);
198 })
Ed Tanouscb13a392020-07-25 19:02:03 +0000199 .onclose([](crow::websocket::Connection& conn, const std::string&) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400200 sessions.erase(&conn);
201 })
Ed Tanous3eb2f352018-12-20 12:30:45 -0800202 .onmessage([](crow::websocket::Connection& conn,
Ed Tanouscb13a392020-07-25 19:02:03 +0000203 const std::string& data, bool) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400204 if (sessions[&conn])
205 {
206 sessions[&conn]->onMessage(data);
207 }
208 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800209}
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700210
Ed Tanous3eb2f352018-12-20 12:30:45 -0800211} // namespace obmc_kvm
212} // namespace crow