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