xref: /openbmc/bmcweb/include/obmc_console.hpp (revision d45d2d0f)
1 #pragma once
2 #include <crow/app.h>
3 #include <crow/websocket.h>
4 #include <sys/socket.h>
5 
6 #include <boost/container/flat_map.hpp>
7 #include <boost/container/flat_set.hpp>
8 #include <webserver_common.hpp>
9 
10 namespace crow
11 {
12 namespace obmc_console
13 {
14 
15 static std::unique_ptr<boost::asio::local::stream_protocol::socket> host_socket;
16 
17 static std::array<char, 4096> outputBuffer;
18 static std::string inputBuffer;
19 
20 static boost::container::flat_set<crow::websocket::Connection*> sessions;
21 
22 static bool doingWrite = false;
23 
24 void doWrite()
25 {
26     if (doingWrite)
27     {
28         BMCWEB_LOG_DEBUG << "Already writing.  Bailing out";
29         return;
30     }
31 
32     if (inputBuffer.empty())
33     {
34         BMCWEB_LOG_DEBUG << "Outbuffer empty.  Bailing out";
35         return;
36     }
37 
38     doingWrite = true;
39     host_socket->async_write_some(
40         boost::asio::buffer(inputBuffer.data(), inputBuffer.size()),
41         [](boost::beast::error_code ec, std::size_t bytes_written) {
42             doingWrite = false;
43             inputBuffer.erase(0, bytes_written);
44 
45             if (ec == boost::asio::error::eof)
46             {
47                 for (auto session : sessions)
48                 {
49                     session->close("Error in reading to host port");
50                 }
51                 return;
52             }
53             if (ec)
54             {
55                 BMCWEB_LOG_ERROR << "Error in host serial write " << ec;
56                 return;
57             }
58             doWrite();
59         });
60 }
61 
62 void doRead()
63 {
64     BMCWEB_LOG_DEBUG << "Reading from socket";
65     host_socket->async_read_some(
66         boost::asio::buffer(outputBuffer.data(), outputBuffer.size()),
67         [](const boost::system::error_code& ec, std::size_t bytesRead) {
68             BMCWEB_LOG_DEBUG << "read done.  Read " << bytesRead << " bytes";
69             if (ec)
70             {
71                 BMCWEB_LOG_ERROR << "Couldn't read from host serial port: "
72                                  << ec;
73                 for (auto session : sessions)
74                 {
75                     session->close("Error in connecting to host port");
76                 }
77                 return;
78             }
79             boost::beast::string_view payload(outputBuffer.data(), bytesRead);
80             for (auto session : sessions)
81             {
82                 session->sendBinary(payload);
83             }
84             doRead();
85         });
86 }
87 
88 void connectHandler(const boost::system::error_code& ec)
89 {
90     if (ec)
91     {
92         BMCWEB_LOG_ERROR << "Couldn't connect to host serial port: " << ec;
93         for (auto session : sessions)
94         {
95             session->close("Error in connecting to host port");
96         }
97         return;
98     }
99 
100     doWrite();
101     doRead();
102 }
103 
104 void requestRoutes(CrowApp& app)
105 {
106     BMCWEB_ROUTE(app, "/console0")
107         .websocket()
108         .onopen([](crow::websocket::Connection& conn) {
109             BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
110 
111             sessions.insert(&conn);
112             if (host_socket == nullptr)
113             {
114                 const std::string consoleName("\0obmc-console", 13);
115                 boost::asio::local::stream_protocol::endpoint ep(consoleName);
116 
117                 host_socket = std::make_unique<
118                     boost::asio::local::stream_protocol::socket>(
119                     conn.getIoService());
120                 host_socket->async_connect(ep, connectHandler);
121             }
122         })
123         .onclose(
124             [](crow::websocket::Connection& conn, const std::string& reason) {
125                 sessions.erase(&conn);
126                 if (sessions.empty())
127                 {
128                     host_socket = nullptr;
129                     inputBuffer.clear();
130                     inputBuffer.shrink_to_fit();
131                 }
132             })
133         .onmessage([](crow::websocket::Connection& conn,
134                       const std::string& data, bool is_binary) {
135             inputBuffer += data;
136             doWrite();
137         });
138 }
139 } // namespace obmc_console
140 } // namespace crow
141