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/asio/local/stream_protocol.hpp> 8 #include <boost/container/flat_map.hpp> 9 #include <boost/container/flat_set.hpp> 10 11 namespace crow 12 { 13 namespace obmc_console 14 { 15 16 static std::unique_ptr<boost::asio::local::stream_protocol::socket> hostSocket; 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 inline 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 hostSocket->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 inline void doRead() 64 { 65 BMCWEB_LOG_DEBUG << "Reading from socket"; 66 hostSocket->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 inline 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 inline void requestRoutes(App& app) 106 { 107 BMCWEB_ROUTE(app, "/console0") 108 .privileges({"ConfigureComponents", "ConfigureManager"}) 109 .websocket() 110 .onopen([](crow::websocket::Connection& conn, 111 std::shared_ptr<bmcweb::AsyncResp>) { 112 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; 113 114 sessions.insert(&conn); 115 if (hostSocket == nullptr) 116 { 117 const std::string consoleName("\0obmc-console", 13); 118 boost::asio::local::stream_protocol::endpoint ep(consoleName); 119 120 hostSocket = std::make_unique< 121 boost::asio::local::stream_protocol::socket>( 122 conn.getIoContext()); 123 hostSocket->async_connect(ep, connectHandler); 124 } 125 }) 126 .onclose([](crow::websocket::Connection& conn, 127 [[maybe_unused]] const std::string& reason) { 128 sessions.erase(&conn); 129 if (sessions.empty()) 130 { 131 hostSocket = nullptr; 132 inputBuffer.clear(); 133 inputBuffer.shrink_to_fit(); 134 } 135 }) 136 .onmessage([]([[maybe_unused]] crow::websocket::Connection& conn, 137 const std::string& data, 138 [[maybe_unused]] bool is_binary) { 139 inputBuffer += data; 140 doWrite(); 141 }); 142 } 143 } // namespace obmc_console 144 } // namespace crow 145