blob: a8866e7194f6a759d60d9f6be9a8349d78551420 [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"
5#include "async_resp.hpp"
Ed Tanousfaf100f2023-05-25 10:03:14 -07006#include "websocket.hpp"
Ed Tanous3ccb3ad2023-01-13 17:40:03 -08007
Ed Tanous3eb2f352018-12-20 12:30:45 -08008#include <sys/socket.h>
9
10#include <boost/container/flat_map.hpp>
Ed Tanous3eb2f352018-12-20 12:30:45 -080011
12namespace crow
13{
14namespace obmc_kvm
15{
16
Jae Hyun Yooa133b292019-09-16 10:38:47 -070017static constexpr const uint maxSessions = 4;
Ed Tanous3eb2f352018-12-20 12:30:45 -080018
Xinnan Xie8e73b902023-08-23 11:14:48 +080019class KvmSession : public std::enable_shared_from_this<KvmSession>
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080020{
Jae Hyun Yooa133b292019-09-16 10:38:47 -070021 public:
Ed Tanous23a21a12020-07-25 04:45:05 +000022 explicit KvmSession(crow::websocket::Connection& connIn) :
Ed Tanousf5b191a2022-02-15 11:30:39 -080023 conn(connIn), hostSocket(conn.getIoContext())
Jae Hyun Yooa133b292019-09-16 10:38:47 -070024 {
25 boost::asio::ip::tcp::endpoint endpoint(
Johnathan Manteyb8c7eb22020-02-19 12:44:19 -080026 boost::asio::ip::make_address("127.0.0.1"), 5900);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070027 hostSocket.async_connect(
Ed Tanous23a21a12020-07-25 04:45:05 +000028 endpoint, [this, &connIn](const boost::system::error_code& ec) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040029 if (ec)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070030 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040031 BMCWEB_LOG_ERROR(
32 "conn:{}, Couldn't connect to KVM socket port: {}",
33 logPtr(&conn), ec);
34 if (ec != boost::asio::error::operation_aborted)
35 {
36 connIn.close("Error in connecting to KVM port");
37 }
38 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -070039 }
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080040
Patrick Williamsbd79bce2024-08-16 15:22:20 -040041 doRead();
42 });
Jae Hyun Yooc68604b2019-02-26 15:46:07 -080043 }
44
Jae Hyun Yooa133b292019-09-16 10:38:47 -070045 void onMessage(const std::string& data)
Ed Tanous3eb2f352018-12-20 12:30:45 -080046 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070047 if (data.length() > inputBuffer.capacity())
Ed Tanous3eb2f352018-12-20 12:30:45 -080048 {
Ed Tanous62598e32023-07-17 17:06:25 -070049 BMCWEB_LOG_ERROR("conn:{}, Buffer overrun when writing {} bytes",
50 logPtr(&conn), data.length());
Jae Hyun Yooa133b292019-09-16 10:38:47 -070051 conn.close("Buffer overrun");
52 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -080053 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070054
Ed Tanous62598e32023-07-17 17:06:25 -070055 BMCWEB_LOG_DEBUG("conn:{}, Read {} bytes from websocket", logPtr(&conn),
56 data.size());
Ed Tanous44106f32024-04-06 13:48:50 -070057 size_t copied = boost::asio::buffer_copy(
58 inputBuffer.prepare(data.size()), boost::asio::buffer(data));
Ed Tanous62598e32023-07-17 17:06:25 -070059 BMCWEB_LOG_DEBUG("conn:{}, Committing {} bytes from websocket",
Ed Tanous44106f32024-04-06 13:48:50 -070060 logPtr(&conn), copied);
61 inputBuffer.commit(copied);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070062
Ed Tanous62598e32023-07-17 17:06:25 -070063 BMCWEB_LOG_DEBUG("conn:{}, inputbuffer size {}", logPtr(&conn),
64 inputBuffer.size());
Jae Hyun Yooa133b292019-09-16 10:38:47 -070065 doWrite();
Ed Tanous3eb2f352018-12-20 12:30:45 -080066 }
67
Jae Hyun Yooa133b292019-09-16 10:38:47 -070068 protected:
69 void doRead()
Ed Tanous3eb2f352018-12-20 12:30:45 -080070 {
Jae Hyun Yooa133b292019-09-16 10:38:47 -070071 std::size_t bytes = outputBuffer.capacity() - outputBuffer.size();
Ed Tanous62598e32023-07-17 17:06:25 -070072 BMCWEB_LOG_DEBUG("conn:{}, Reading {} from kvm socket", logPtr(&conn),
73 bytes);
Jae Hyun Yooa133b292019-09-16 10:38:47 -070074 hostSocket.async_read_some(
75 outputBuffer.prepare(outputBuffer.capacity() - outputBuffer.size()),
Xinnan Xie8e73b902023-08-23 11:14:48 +080076 [this, weak(weak_from_this())](const boost::system::error_code& ec,
77 std::size_t bytesRead) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040078 auto self = weak.lock();
79 if (self == nullptr)
Jae Hyun Yooa133b292019-09-16 10:38:47 -070080 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -040081 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -070082 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -040083 BMCWEB_LOG_DEBUG("conn:{}, read done. Read {} bytes",
84 logPtr(&conn), bytesRead);
85 if (ec)
86 {
87 BMCWEB_LOG_ERROR(
88 "conn:{}, Couldn't read from KVM socket port: {}",
89 logPtr(&conn), ec);
90 if (ec != boost::asio::error::operation_aborted)
91 {
92 conn.close("Error in connecting to KVM port");
93 }
94 return;
95 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -070096
Patrick Williamsbd79bce2024-08-16 15:22:20 -040097 outputBuffer.commit(bytesRead);
98 std::string_view payload(
99 static_cast<const char*>(outputBuffer.data().data()),
100 bytesRead);
101 BMCWEB_LOG_DEBUG("conn:{}, Sending payload size {}",
102 logPtr(&conn), payload.size());
103 conn.sendBinary(payload);
104 outputBuffer.consume(bytesRead);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700105
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400106 doRead();
107 });
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700108 }
109
110 void doWrite()
111 {
112 if (doingWrite)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800113 {
Ed Tanous62598e32023-07-17 17:06:25 -0700114 BMCWEB_LOG_DEBUG("conn:{}, Already writing. Bailing out",
115 logPtr(&conn));
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700116 return;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800117 }
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700118 if (inputBuffer.size() == 0)
119 {
Ed Tanous62598e32023-07-17 17:06:25 -0700120 BMCWEB_LOG_DEBUG("conn:{}, inputBuffer empty. Bailing out",
121 logPtr(&conn));
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700122 return;
123 }
124
125 doingWrite = true;
Xinnan Xie8e73b902023-08-23 11:14:48 +0800126 hostSocket.async_write_some(
127 inputBuffer.data(),
128 [this, weak(weak_from_this())](const boost::system::error_code& ec,
Ed Tanous002d39b2022-05-31 08:59:27 -0700129 std::size_t bytesWritten) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400130 auto self = weak.lock();
131 if (self == nullptr)
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700132 {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400133 return;
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700134 }
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400135 BMCWEB_LOG_DEBUG("conn:{}, Wrote {}bytes", logPtr(&conn),
136 bytesWritten);
137 doingWrite = false;
138 inputBuffer.consume(bytesWritten);
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700139
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400140 if (ec == boost::asio::error::eof)
141 {
142 conn.close("KVM socket port closed");
143 return;
144 }
145 if (ec)
146 {
147 BMCWEB_LOG_ERROR("conn:{}, Error in KVM socket write {}",
148 logPtr(&conn), ec);
149 if (ec != boost::asio::error::operation_aborted)
150 {
151 conn.close("Error in reading to host port");
152 }
153 return;
154 }
155
156 doWrite();
157 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800158 }
159
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700160 crow::websocket::Connection& conn;
161 boost::asio::ip::tcp::socket hostSocket;
Ed Tanous6de264c2022-01-06 12:47:59 -0800162 boost::beast::flat_static_buffer<1024UL * 50UL> outputBuffer;
163 boost::beast::flat_static_buffer<1024UL> inputBuffer;
Ed Tanousf5b191a2022-02-15 11:30:39 -0800164 bool doingWrite{false};
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700165};
166
Ed Tanouscf9e4172022-12-21 09:30:16 -0800167using SessionMap = boost::container::flat_map<crow::websocket::Connection*,
Xinnan Xie8e73b902023-08-23 11:14:48 +0800168 std::shared_ptr<KvmSession>>;
Ed Tanouscf9e4172022-12-21 09:30:16 -0800169// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
170static SessionMap sessions;
Ed Tanous3eb2f352018-12-20 12:30:45 -0800171
Ed Tanous52cc1122020-07-18 13:51:21 -0700172inline void requestRoutes(App& app)
Ed Tanous3eb2f352018-12-20 12:30:45 -0800173{
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700174 sessions.reserve(maxSessions);
175
Ed Tanous3eb2f352018-12-20 12:30:45 -0800176 BMCWEB_ROUTE(app, "/kvm/0")
Ed Tanous432a8902021-06-14 15:28:56 -0700177 .privileges({{"ConfigureComponents", "ConfigureManager"}})
Ed Tanous3eb2f352018-12-20 12:30:45 -0800178 .websocket()
zhanghch0577726382021-10-21 14:07:57 +0800179 .onopen([](crow::websocket::Connection& conn) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400180 BMCWEB_LOG_DEBUG("Connection {} opened", logPtr(&conn));
Ed Tanous3eb2f352018-12-20 12:30:45 -0800181
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400182 if (sessions.size() == maxSessions)
183 {
184 conn.close("Max sessions are already connected");
185 return;
186 }
Ed Tanous3eb2f352018-12-20 12:30:45 -0800187
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400188 sessions[&conn] = std::make_shared<KvmSession>(conn);
189 })
Ed Tanouscb13a392020-07-25 19:02:03 +0000190 .onclose([](crow::websocket::Connection& conn, const std::string&) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400191 sessions.erase(&conn);
192 })
Ed Tanous3eb2f352018-12-20 12:30:45 -0800193 .onmessage([](crow::websocket::Connection& conn,
Ed Tanouscb13a392020-07-25 19:02:03 +0000194 const std::string& data, bool) {
Patrick Williamsbd79bce2024-08-16 15:22:20 -0400195 if (sessions[&conn])
196 {
197 sessions[&conn]->onMessage(data);
198 }
199 });
Ed Tanous3eb2f352018-12-20 12:30:45 -0800200}
Jae Hyun Yooa133b292019-09-16 10:38:47 -0700201
Ed Tanous3eb2f352018-12-20 12:30:45 -0800202} // namespace obmc_kvm
203} // namespace crow