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 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 inline 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 (crow::websocket::Connection* 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 inline 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 (crow::websocket::Connection* session : sessions) 74 { 75 session->close("Error in connecting to host port"); 76 } 77 return; 78 } 79 std::string_view payload(outputBuffer.data(), bytesRead); 80 for (crow::websocket::Connection* session : sessions) 81 { 82 session->sendBinary(payload); 83 } 84 doRead(); 85 }); 86 } 87 88 inline 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 (crow::websocket::Connection* session : sessions) 94 { 95 session->close("Error in connecting to host port"); 96 } 97 return; 98 } 99 100 doWrite(); 101 doRead(); 102 } 103 104 inline void requestRoutes(App& app) 105 { 106 BMCWEB_ROUTE(app, "/console0") 107 .privileges({"ConfigureComponents", "ConfigureManager"}) 108 .websocket() 109 .onopen([](crow::websocket::Connection& conn, 110 std::shared_ptr<bmcweb::AsyncResp> asyncResp) { 111 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; 112 113 sessions.insert(&conn); 114 if (host_socket == nullptr) 115 { 116 const std::string consoleName("\0obmc-console", 13); 117 boost::asio::local::stream_protocol::endpoint ep(consoleName); 118 119 host_socket = std::make_unique< 120 boost::asio::local::stream_protocol::socket>( 121 conn.get_io_context()); 122 host_socket->async_connect(ep, connectHandler); 123 } 124 }) 125 .onclose( 126 [](crow::websocket::Connection& conn, const std::string& reason) { 127 sessions.erase(&conn); 128 if (sessions.empty()) 129 { 130 host_socket = nullptr; 131 inputBuffer.clear(); 132 inputBuffer.shrink_to_fit(); 133 } 134 }) 135 .onmessage([](crow::websocket::Connection& conn, 136 const std::string& data, bool is_binary) { 137 inputBuffer += data; 138 doWrite(); 139 }); 140 } 141 } // namespace obmc_console 142 } // namespace crow 143