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