blob: adf4c5e02d7f1487d4bab6bcdd509bb456a57057 [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 Tanousd98a2f92025-02-06 17:36:31 -08005#include "io_context_singleton.hpp"
Ed Tanousd7857202025-01-28 15:32:26 -08006#include "logging.hpp"
Ed Tanousfaf100f2023-05-25 10:03:14 -07007#include "websocket.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08008
Ed Tanousd7857202025-01-28 15:32:26 -08009#include <sys/types.h>
Ed Tanous3eb2f352018-12-20 12:30:45 -080010
Ed Tanousd7857202025-01-28 15:32:26 -080011#include <boost/asio/buffer.hpp>
12#include <boost/asio/error.hpp>
13#include <boost/asio/ip/address.hpp>
14#include <boost/asio/ip/tcp.hpp>
15#include <boost/beast/core/flat_static_buffer.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -080016#include <boost/container/flat_map.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -080017
Ed Tanousd7857202025-01-28 15:32:26 -080018#include <cstddef>
19#include <memory>
20#include <string>
21
Ed Tanous3eb2f352018-12-20 12:30:45 -080022namespace crow
23{
24namespace obmc_kvm
25{
26
Jae Hyun Yooa133b292019-09-16 10:38:47 -070027static constexpr const uint maxSessions = 4;
Ed Tanous3eb2f352018-12-20 12:30:45 -080028
Xinnan Xie8e73b902023-08-23 11:14:48 +080029class KvmSession : public std::enable_shared_from_this<KvmSession>
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080030{
Jae Hyun Yooa133b292019-09-16 10:38:47 -070031 public:
Ed Tanous23a21a12020-07-25 04:45:05 +000032 explicit KvmSession(crow::websocket::Connection& connIn) :
Ed Tanousd98a2f92025-02-06 17:36:31 -080033 conn(connIn), hostSocket(getIoContext())
Jae Hyun Yooa133b292019-09-16 10:38:47 -070034 {
35 boost::asio::ip::tcp::endpoint endpoint(
Johnathan Manteyb8c7eb22020-02-19 12:44:19 -080036 boost::asio::ip::make_address("127.0.0.1"), 5900);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070037 hostSocket.async_connect(
Ed Tanous23a21a12020-07-25 04:45:05 +000038 endpoint, [this, &connIn](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040039 if (ec)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070040 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040041 BMCWEB_LOG_ERROR(
42 "conn:{}, Couldn't connect to KVM socket port: {}",
43 logPtr(&conn), ec);
44 if (ec != boost::asio::error::operation_aborted)
45 {
46 connIn.close("Error in connecting to KVM port");
47 }
48 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -070049 }
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080050
Patrick Williamsbd79bce2024-08-16 15:22:20 -040051 doRead();
52 });
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080053 }
54
Jae Hyun Yooa133b292019-09-16 10:38:47 -070055 void onMessage(const std::string& data)
Ed Tanous3eb2f352018-12-20 12:30:45 -080056 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070057 if (data.length() > inputBuffer.capacity())
Ed Tanous3eb2f352018-12-20 12:30:45 -080058 {
Ed Tanous62598e32023-07-17 17:06:25 -070059 BMCWEB_LOG_ERROR("conn:{}, Buffer overrun when writing {} bytes",
60 logPtr(&conn), data.length());
Jae Hyun Yooa133b292019-09-16 10:38:47 -070061 conn.close("Buffer overrun");
62 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -080063 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070064
Ed Tanous62598e32023-07-17 17:06:25 -070065 BMCWEB_LOG_DEBUG("conn:{}, Read {} bytes from websocket", logPtr(&conn),
66 data.size());
Ed Tanous44106f32024-04-06 13:48:50 -070067 size_t copied = boost::asio::buffer_copy(
68 inputBuffer.prepare(data.size()), boost::asio::buffer(data));
Ed Tanous62598e32023-07-17 17:06:25 -070069 BMCWEB_LOG_DEBUG("conn:{}, Committing {} bytes from websocket",
Ed Tanous44106f32024-04-06 13:48:50 -070070 logPtr(&conn), copied);
71 inputBuffer.commit(copied);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070072
Ed Tanous62598e32023-07-17 17:06:25 -070073 BMCWEB_LOG_DEBUG("conn:{}, inputbuffer size {}", logPtr(&conn),
74 inputBuffer.size());
Jae Hyun Yooa133b292019-09-16 10:38:47 -070075 doWrite();
Ed Tanous3eb2f352018-12-20 12:30:45 -080076 }
77
Jae Hyun Yooa133b292019-09-16 10:38:47 -070078 protected:
79 void doRead()
Ed Tanous3eb2f352018-12-20 12:30:45 -080080 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070081 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Ed Tanous62598e32023-07-17 17:06:25 -070082 BMCWEB_LOG_DEBUG("conn:{}, Reading {} from kvm socket", logPtr(&conn),
83 bytes);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070084 hostSocket.async_read_some(
85 outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
Xinnan Xie8e73b902023-08-23 11:14:48 +080086 [this, weak(weak_from_this())](const boost::system::error_code& ec,
87 std::size_t bytesRead) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040088 auto self = weak.lock();
89 if (self == nullptr)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070090 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040091 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -070092 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -040093 BMCWEB_LOG_DEBUG("conn:{}, read done. Read {} bytes",
94 logPtr(&conn), bytesRead);
95 if (ec)
96 {
97 BMCWEB_LOG_ERROR(
98 "conn:{}, Couldn't read from KVM socket port: {}",
99 logPtr(&conn), ec);
100 if (ec != boost::asio::error::operation_aborted)
101 {
102 conn.close("Error in connecting to KVM port");
103 }
104 return;
105 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700106
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400107 outputBuffer.commit(bytesRead);
108 std::string_view payload(
109 static_cast<const char*>(outputBuffer.data().data()),
110 bytesRead);
111 BMCWEB_LOG_DEBUG("conn:{}, Sending payload size {}",
112 logPtr(&conn), payload.size());
113 conn.sendBinary(payload);
114 outputBuffer.consume(bytesRead);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700115
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400116 doRead();
117 });
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700118 }
119
120 void doWrite()
121 {
122 if (doingWrite)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800123 {
Ed Tanous62598e32023-07-17 17:06:25 -0700124 BMCWEB_LOG_DEBUG("conn:{}, Already writing. Bailing out",
125 logPtr(&conn));
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700126 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800127 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700128 if (inputBuffer.size() == 0)
129 {
Ed Tanous62598e32023-07-17 17:06:25 -0700130 BMCWEB_LOG_DEBUG("conn:{}, inputBuffer empty. Bailing out",
131 logPtr(&conn));
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700132 return;
133 }
134
135 doingWrite = true;
Xinnan Xie8e73b902023-08-23 11:14:48 +0800136 hostSocket.async_write_some(
137 inputBuffer.data(),
138 [this, weak(weak_from_this())](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700139 std::size_t bytesWritten) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400140 auto self = weak.lock();
141 if (self == nullptr)
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700142 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400143 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700144 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400145 BMCWEB_LOG_DEBUG("conn:{}, Wrote {}bytes", logPtr(&conn),
146 bytesWritten);
147 doingWrite = false;
148 inputBuffer.consume(bytesWritten);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700149
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400150 if (ec == boost::asio::error::eof)
151 {
152 conn.close("KVM socket port closed");
153 return;
154 }
155 if (ec)
156 {
157 BMCWEB_LOG_ERROR("conn:{}, Error in KVM socket write {}",
158 logPtr(&conn), ec);
159 if (ec != boost::asio::error::operation_aborted)
160 {
161 conn.close("Error in reading to host port");
162 }
163 return;
164 }
165
166 doWrite();
167 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800168 }
169
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700170 crow::websocket::Connection& conn;
171 boost::asio::ip::tcp::socket hostSocket;
Ed Tanous6de264c2022-01-06 12:47:59 -0800172 boost::beast::flat_static_buffer<1024UL * 50UL> outputBuffer;
173 boost::beast::flat_static_buffer<1024UL> inputBuffer;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800174 bool doingWrite{false};
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700175};
176
Ed Tanouscf9e4172022-12-21 09:30:16 -0800177using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
Xinnan Xie8e73b902023-08-23 11:14:48 +0800178 std::shared_ptr<KvmSession>>;
Ed Tanouscf9e4172022-12-21 09:30:16 -0800179// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
180static SessionMap sessions;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800181
Ed Tanous52cc1122020-07-18 13:51:21 -0700182inline void requestRoutes(App& app)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800183{
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700184 sessions.reserve(maxSessions);
185
Ed Tanous3eb2f352018-12-20 12:30:45 -0800186 BMCWEB_ROUTE(app, "/kvm/0")
Ed Tanous432a8902021-06-14 15:28:56 -0700187 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous3eb2f352018-12-20 12:30:45 -0800188 .websocket()
zhanghch0577726382021-10-21 14:07:57 +0800189 .onopen([](crow::websocket::Connection& conn) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400190 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanous3eb2f352018-12-20 12:30:45 -0800191
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400192 if (sessions.size() == maxSessions)
193 {
194 conn.close("Max sessions are already connected");
195 return;
196 }
Ed Tanous3eb2f352018-12-20 12:30:45 -0800197
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400198 sessions[&conn] = std::make_shared<KvmSession>(conn);
199 })
Ed Tanouscb13a392020-07-25 19:02:03 +0000200 .onclose([](crow::websocket::Connection& conn, const std::string&) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400201 sessions.erase(&conn);
202 })
Ed Tanous3eb2f352018-12-20 12:30:45 -0800203 .onmessage([](crow::websocket::Connection& conn,
Ed Tanouscb13a392020-07-25 19:02:03 +0000204 const std::string& data, bool) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400205 if (sessions[&conn])
206 {
207 sessions[&conn]->onMessage(data);
208 }
209 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800210}
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700211
Ed Tanous3eb2f352018-12-20 12:30:45 -0800212} // namespace obmc_kvm
213} // namespace crow