1 #pragma once 2 #include <sys/socket.h> 3 4 #include <app.hpp> 5 #include <async_resp.hpp> 6 #include <boost/asio/local/stream_protocol.hpp> 7 #include <boost/container/flat_set.hpp> 8 #include <websocket.hpp> 9 10 namespace crow 11 { 12 namespace obmc_console 13 { 14 15 static std::unique_ptr<boost::asio::local::stream_protocol::socket> hostSocket; 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 if (!hostSocket) 39 { 40 BMCWEB_LOG_ERROR << "doWrite(): Socket closed."; 41 return; 42 } 43 44 doingWrite = true; 45 hostSocket->async_write_some( 46 boost::asio::buffer(inputBuffer.data(), inputBuffer.size()), 47 [](boost::beast::error_code ec, std::size_t bytesWritten) { 48 doingWrite = false; 49 inputBuffer.erase(0, bytesWritten); 50 51 if (ec == boost::asio::error::eof) 52 { 53 for (crow::websocket::Connection* session : sessions) 54 { 55 session->close("Error in reading to host port"); 56 } 57 return; 58 } 59 if (ec) 60 { 61 BMCWEB_LOG_ERROR << "Error in host serial write " << ec; 62 return; 63 } 64 doWrite(); 65 }); 66 } 67 68 inline void doRead() 69 { 70 if (!hostSocket) 71 { 72 BMCWEB_LOG_ERROR << "doRead(): Socket closed."; 73 return; 74 } 75 76 BMCWEB_LOG_DEBUG << "Reading from socket"; 77 hostSocket->async_read_some( 78 boost::asio::buffer(outputBuffer.data(), outputBuffer.size()), 79 [](const boost::system::error_code& ec, std::size_t bytesRead) { 80 BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes"; 81 if (ec) 82 { 83 BMCWEB_LOG_ERROR << "Couldn't read from host serial port: " << ec; 84 for (crow::websocket::Connection* session : sessions) 85 { 86 session->close("Error in connecting to host port"); 87 } 88 return; 89 } 90 std::string_view payload(outputBuffer.data(), bytesRead); 91 for (crow::websocket::Connection* session : sessions) 92 { 93 session->sendBinary(payload); 94 } 95 doRead(); 96 }); 97 } 98 99 inline void connectHandler(const boost::system::error_code& ec) 100 { 101 if (ec) 102 { 103 BMCWEB_LOG_ERROR << "Couldn't connect to host serial port: " << ec; 104 for (crow::websocket::Connection* session : sessions) 105 { 106 session->close("Error in connecting to host port"); 107 } 108 return; 109 } 110 111 doWrite(); 112 doRead(); 113 } 114 115 inline void requestRoutes(App& app) 116 { 117 BMCWEB_ROUTE(app, "/console0") 118 .privileges({{"ConfigureComponents", "ConfigureManager"}}) 119 .websocket() 120 .onopen( 121 [](crow::websocket::Connection& conn) { 122 BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; 123 124 sessions.insert(&conn); 125 if (hostSocket == nullptr) 126 { 127 const std::string consoleName("\0obmc-console", 13); 128 boost::asio::local::stream_protocol::endpoint ep(consoleName); 129 130 hostSocket = 131 std::make_unique<boost::asio::local::stream_protocol::socket>( 132 conn.getIoContext()); 133 hostSocket->async_connect(ep, connectHandler); 134 } 135 }) 136 .onclose([](crow::websocket::Connection& conn, 137 [[maybe_unused]] const std::string& reason) { 138 BMCWEB_LOG_INFO << "Closing websocket. Reason: " << reason; 139 140 sessions.erase(&conn); 141 if (sessions.empty()) 142 { 143 hostSocket = nullptr; 144 inputBuffer.clear(); 145 inputBuffer.shrink_to_fit(); 146 } 147 }) 148 .onmessage([]([[maybe_unused]] crow::websocket::Connection& conn, 149 const std::string& data, [[maybe_unused]] bool isBinary) { 150 inputBuffer += data; 151 doWrite(); 152 }); 153 } 154 } // namespace obmc_console 155 } // namespace crow 156