blob: 5457b7d5b4ce9f5c9be2b06ec82f532c8f140719 [file] [log] [blame]
Ed Tanous2daf6722018-08-23 11:27:23 -07001#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 Tanous2daf6722018-08-23 11:27:23 -07006#include <sys/socket.h>
7
Ed Tanousd4d77e32020-08-18 00:07:28 -07008#include <boost/asio/local/stream_protocol.hpp>
Ed Tanous2daf6722018-08-23 11:27:23 -07009#include <boost/container/flat_set.hpp>
Ed Tanous2daf6722018-08-23 11:27:23 -070010
11namespace crow
12{
13namespace obmc_console
14{
15
Ed Tanouscf9e4172022-12-21 09:30:16 -080016// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous2c70f802020-09-28 14:29:23 -070017static std::unique_ptr<boost::asio::local::stream_protocol::socket> hostSocket;
Ed Tanous2daf6722018-08-23 11:27:23 -070018
Ed Tanouscf9e4172022-12-21 09:30:16 -080019// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous2daf6722018-08-23 11:27:23 -070020static std::array<char, 4096> outputBuffer;
Ed Tanouscf9e4172022-12-21 09:30:16 -080021// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous2daf6722018-08-23 11:27:23 -070022static std::string inputBuffer;
23
Ed Tanouscf9e4172022-12-21 09:30:16 -080024// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous2daf6722018-08-23 11:27:23 -070025static boost::container::flat_set<crow::websocket::Connection*> sessions;
26
Ed Tanouscf9e4172022-12-21 09:30:16 -080027// NOLINTNEXTLINE(cppcoreguidelines-avoid-non-const-global-variables)
Ed Tanous2daf6722018-08-23 11:27:23 -070028static bool doingWrite = false;
29
Ed Tanous23a21a12020-07-25 04:45:05 +000030inline void doWrite()
Ed Tanous2daf6722018-08-23 11:27:23 -070031{
32 if (doingWrite)
33 {
34 BMCWEB_LOG_DEBUG << "Already writing. Bailing out";
35 return;
36 }
37
38 if (inputBuffer.empty())
39 {
40 BMCWEB_LOG_DEBUG << "Outbuffer empty. Bailing out";
41 return;
42 }
43
AppaRao Puli5238bd32020-11-17 11:03:11 +053044 if (!hostSocket)
45 {
46 BMCWEB_LOG_ERROR << "doWrite(): Socket closed.";
47 return;
48 }
49
Ed Tanous2daf6722018-08-23 11:27:23 -070050 doingWrite = true;
Ed Tanous2c70f802020-09-28 14:29:23 -070051 hostSocket->async_write_some(
Ed Tanous2daf6722018-08-23 11:27:23 -070052 boost::asio::buffer(inputBuffer.data(), inputBuffer.size()),
Ed Tanous81c4e332023-05-18 10:30:34 -070053 [](const boost::beast::error_code& ec, std::size_t bytesWritten) {
Ed Tanous002d39b2022-05-31 08:59:27 -070054 doingWrite = false;
55 inputBuffer.erase(0, bytesWritten);
Ed Tanous2daf6722018-08-23 11:27:23 -070056
Ed Tanous002d39b2022-05-31 08:59:27 -070057 if (ec == boost::asio::error::eof)
58 {
59 for (crow::websocket::Connection* session : sessions)
Ed Tanous2daf6722018-08-23 11:27:23 -070060 {
Ed Tanous002d39b2022-05-31 08:59:27 -070061 session->close("Error in reading to host port");
Ed Tanous2daf6722018-08-23 11:27:23 -070062 }
Ed Tanous002d39b2022-05-31 08:59:27 -070063 return;
64 }
65 if (ec)
66 {
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -050067 BMCWEB_LOG_ERROR << "Error in host serial write " << ec.message();
Ed Tanous002d39b2022-05-31 08:59:27 -070068 return;
69 }
70 doWrite();
Ed Tanous2daf6722018-08-23 11:27:23 -070071 });
72}
73
Ed Tanous23a21a12020-07-25 04:45:05 +000074inline void doRead()
Ed Tanous2daf6722018-08-23 11:27:23 -070075{
AppaRao Puli5238bd32020-11-17 11:03:11 +053076 if (!hostSocket)
77 {
78 BMCWEB_LOG_ERROR << "doRead(): Socket closed.";
79 return;
80 }
81
Ed Tanous2daf6722018-08-23 11:27:23 -070082 BMCWEB_LOG_DEBUG << "Reading from socket";
Ed Tanous2c70f802020-09-28 14:29:23 -070083 hostSocket->async_read_some(
Ed Tanous2daf6722018-08-23 11:27:23 -070084 boost::asio::buffer(outputBuffer.data(), outputBuffer.size()),
85 [](const boost::system::error_code& ec, std::size_t bytesRead) {
Ed Tanous002d39b2022-05-31 08:59:27 -070086 BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes";
87 if (ec)
88 {
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -050089 BMCWEB_LOG_ERROR << "Couldn't read from host serial port: "
90 << ec.message();
Ed Tanous271584a2019-07-09 16:24:22 -070091 for (crow::websocket::Connection* session : sessions)
Ed Tanous2daf6722018-08-23 11:27:23 -070092 {
Ed Tanous002d39b2022-05-31 08:59:27 -070093 session->close("Error in connecting to host port");
Ed Tanous2daf6722018-08-23 11:27:23 -070094 }
Ed Tanous002d39b2022-05-31 08:59:27 -070095 return;
96 }
97 std::string_view payload(outputBuffer.data(), bytesRead);
98 for (crow::websocket::Connection* session : sessions)
99 {
100 session->sendBinary(payload);
101 }
102 doRead();
Ed Tanous2daf6722018-08-23 11:27:23 -0700103 });
104}
105
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -0500106// If connection is active then remove it from the connection map
107inline bool removeConnection(crow::websocket::Connection& conn)
Ed Tanous2daf6722018-08-23 11:27:23 -0700108{
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -0500109 bool ret = false;
110
111 if (sessions.erase(&conn) != 0U)
112 {
113 ret = true;
114 }
115
116 if (sessions.empty())
117 {
118 hostSocket = nullptr;
119 inputBuffer.clear();
120 inputBuffer.shrink_to_fit();
121 }
122 return ret;
123}
124
125inline void connectConsoleSocket(crow::websocket::Connection& conn,
126 const boost::system::error_code& ec,
127 const sdbusplus::message::unix_fd& unixfd)
128{
129 int fd = -1;
130
Ed Tanous2daf6722018-08-23 11:27:23 -0700131 if (ec)
132 {
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -0500133 BMCWEB_LOG_ERROR << "Failed to call console Connect() method"
134 << " DBUS error: " << ec.message();
135 if (removeConnection(conn))
Ed Tanous2daf6722018-08-23 11:27:23 -0700136 {
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -0500137 conn.close("Failed to call console Connect() method");
Ed Tanous2daf6722018-08-23 11:27:23 -0700138 }
139 return;
140 }
141
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -0500142 // Make sure that connection is still open.
143 if (!sessions.contains(&conn))
144 {
145 return;
146 }
147
148 fd = dup(unixfd);
149 if (fd == -1)
150 {
151 BMCWEB_LOG_ERROR << "Failed to dup the DBUS unixfd"
152 << " error: " << strerror(errno);
153 if (removeConnection(conn))
154 {
155 conn.close("Failed to dup the DBUS unixfd");
156 }
157 return;
158 }
159
160 BMCWEB_LOG_DEBUG << "Console web socket path: " << conn.req.target()
161 << " Console unix FD: " << unixfd << " duped FD: " << fd;
162
163 if (hostSocket == nullptr)
164 {
165 boost::system::error_code ec1;
166 boost::asio::local::stream_protocol proto;
167 hostSocket =
168 std::make_unique<boost::asio::local::stream_protocol::socket>(
169 conn.getIoContext());
170
171 hostSocket->assign(proto, fd, ec1);
172
173 if (ec1)
174 {
175 close(fd);
176 BMCWEB_LOG_ERROR << "Failed to assign the DBUS socket"
177 << " Socket assign error: " << ec1.message();
178 if (removeConnection(conn))
179 {
180 conn.close("Failed to assign the DBUS socket");
181 }
182 }
183 else
184 {
185 conn.resumeRead();
186 doWrite();
187 doRead();
188 }
189 }
190 else
191 {
192 BMCWEB_LOG_DEBUG << "Socket already exist so close the new fd: " << fd;
193 close(fd);
194 }
195}
196
197// Query consoles from DBUS and find the matching to the
198// rules string.
199inline void onOpen(crow::websocket::Connection& conn)
200{
201 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
202
203 // Save the connection in the map
204 sessions.insert(&conn);
205
206 // We need to wait for dbus and the websockets to hook up before data is
207 // sent/received. Tell the core to hold off messages until the sockets are
208 // up
209 if (hostSocket == nullptr)
210 {
211 conn.deferRead();
212 }
213
214 // The console id 'default' is used for the console0
215 // We need to change it when we provide full multi-console support.
216 const std::string consolePath = "/xyz/openbmc_project/console/default";
217 const std::string consoleService = "xyz.openbmc_project.Console.default";
218
219 BMCWEB_LOG_DEBUG << "Console Object path = " << consolePath
220 << " service = " << consoleService
221 << " Request target = " << conn.req.target();
222
223 // Call Connect() method to get the unix FD
224 crow::connections::systemBus->async_method_call(
225 [&conn](const boost::system::error_code& ec,
226 const sdbusplus::message::unix_fd& unixfd) {
227 connectConsoleSocket(conn, ec, unixfd);
228 },
229 consoleService, consolePath, "xyz.openbmc_project.Console.Access",
230 "Connect");
Ed Tanous2daf6722018-08-23 11:27:23 -0700231}
232
Ed Tanous23a21a12020-07-25 04:45:05 +0000233inline void requestRoutes(App& app)
Ed Tanous2daf6722018-08-23 11:27:23 -0700234{
235 BMCWEB_ROUTE(app, "/console0")
Ninad Palsule3e72c202023-03-27 17:19:55 -0500236 .privileges({{"OpenBMCHostConsole"}})
Ed Tanous2daf6722018-08-23 11:27:23 -0700237 .websocket()
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -0500238 .onopen(onOpen)
Ed Tanouscb13a392020-07-25 19:02:03 +0000239 .onclose([](crow::websocket::Connection& conn,
240 [[maybe_unused]] const std::string& reason) {
AppaRao Puli5238bd32020-11-17 11:03:11 +0530241 BMCWEB_LOG_INFO << "Closing websocket. Reason: " << reason;
242
Ninad Palsulea8d4b8e2023-04-10 16:40:00 -0500243 removeConnection(conn);
Ed Tanouscb13a392020-07-25 19:02:03 +0000244 })
245 .onmessage([]([[maybe_unused]] crow::websocket::Connection& conn,
Ed Tanous81ce6092020-12-17 16:54:55 +0000246 const std::string& data, [[maybe_unused]] bool isBinary) {
Ed Tanous2daf6722018-08-23 11:27:23 -0700247 inputBuffer += data;
248 doWrite();
249 });
250}
251} // namespace obmc_console
252} // namespace crow