diff options
author | Ed Tanous <ed.tanous@intel.com> | 2018-08-23 21:27:23 +0300 |
---|---|---|
committer | Ed Tanous <ed.tanous@intel.com> | 2018-09-05 18:51:19 +0300 |
commit | 2daf6725629dc9fae0ba554ddef0577fee60ef09 (patch) | |
tree | 26c2c8c4965a039af2e811bff09e4eca218308b6 /include/obmc_console.hpp | |
parent | 1abe55ef9844afcddcab9d862ae06118f3a2390c (diff) | |
download | bmcweb-2daf6725629dc9fae0ba554ddef0577fee60ef09.tar.xz |
Implement /console0 websocket
This commit ipmlements the serial console websocket in a way that is
compatible with phosphor-rest. This allows the webui serial console to
function. Latency doesn't appear improved, but I suspect that the
obmc-console server has issues.
Tested By:
Booted phosphor-webui serial console. Serial console works as
expected. Also implemented a serial console in python using python
websocket, and it appears to send and receive data correctly.
Change-Id: I0e571beb70a51923d6d7d148779a1154432c45c9
Signed-off-by: Ed Tanous <ed.tanous@intel.com>
Diffstat (limited to 'include/obmc_console.hpp')
-rw-r--r-- | include/obmc_console.hpp | 157 |
1 files changed, 157 insertions, 0 deletions
diff --git a/include/obmc_console.hpp b/include/obmc_console.hpp new file mode 100644 index 0000000000..a15004ea65 --- /dev/null +++ b/include/obmc_console.hpp @@ -0,0 +1,157 @@ +#pragma once +#include <crow/app.h> +#include <crow/websocket.h> +#include <sys/socket.h> + +#include <boost/container/flat_map.hpp> +#include <boost/container/flat_set.hpp> +#include <webserver_common.hpp> + +namespace crow +{ +namespace obmc_console +{ + +static std::unique_ptr<boost::asio::local::stream_protocol::socket> host_socket; + +static std::array<char, 4096> outputBuffer; +static std::string inputBuffer; + +static boost::container::flat_set<crow::websocket::Connection*> sessions; + +static bool doingWrite = false; + +void doWrite() +{ + if (doingWrite) + { + BMCWEB_LOG_DEBUG << "Already writing. Bailing out"; + return; + } + + if (inputBuffer.empty()) + { + BMCWEB_LOG_DEBUG << "Outbuffer empty. Bailing out"; + return; + } + + doingWrite = true; + host_socket->async_write_some( + boost::asio::buffer(inputBuffer.data(), inputBuffer.size()), + [](boost::beast::error_code ec, std::size_t bytes_written) { + doingWrite = false; + inputBuffer.erase(0, bytes_written); + + if (ec == boost::asio::error::eof) + { + for (auto session : sessions) + { + session->close("Error in reading to host port"); + } + return; + } + if (ec) + { + BMCWEB_LOG_ERROR << "Error in host serial write " << ec; + return; + } + doWrite(); + }); +} + +void doRead() +{ + BMCWEB_LOG_DEBUG << "Reading from socket"; + host_socket->async_read_some( + boost::asio::buffer(outputBuffer.data(), outputBuffer.size()), + [](const boost::system::error_code& ec, std::size_t bytesRead) { + BMCWEB_LOG_DEBUG << "read done. Read " << bytesRead << " bytes"; + if (ec) + { + BMCWEB_LOG_ERROR << "Couldn't read from host serial port: " + << ec; + for (auto session : sessions) + { + session->close("Error in connecting to host port"); + } + return; + } + boost::beast::string_view payload(outputBuffer.data(), bytesRead); + for (auto session : sessions) + { + session->sendText(payload); + } + doRead(); + }); +} + +void connectHandler(const boost::system::error_code& ec) +{ + if (ec) + { + BMCWEB_LOG_ERROR << "Couldn't connect to host serial port: " << ec; + for (auto session : sessions) + { + session->close("Error in connecting to host port"); + } + return; + } + + doWrite(); + doRead(); +} + +void requestRoutes(CrowApp& app) +{ + BMCWEB_ROUTE(app, "/console0") + .websocket() + .onopen([](crow::websocket::Connection& conn) { + BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened"; + + sessions.insert(&conn); + if (host_socket == nullptr) + { + const std::string consoleName("\0obmc-console", 13); + boost::asio::local::stream_protocol::endpoint ep(consoleName); + + // This is a hack. For whatever reason boost local endpoint has + // a check to see if a string is null terminated, and if it is, + // it drops the path character count by 1. For abstract + // sockets, we need the count to be the full sizeof(s->sun_path) + // (ie 108), even though our path _looks_ like it's null + // terminated. This is likely a bug in asio that needs to be + // submitted Todo(ed). so the cheat here is to break the + // abstraction for a minute, write a 1 to the last byte, this + // causes the check at the end of resize here: + // https://www.boost.org/doc/libs/1_68_0/boost/asio/local/detail/impl/endpoint.ipp + // to not decrement us unesssesarily. + struct sockaddr_un* s = + reinterpret_cast<sockaddr_un*>(ep.data()); + s->sun_path[sizeof(s->sun_path) - 1] = 1; + ep.resize(sizeof(sockaddr_un)); + s->sun_path[sizeof(s->sun_path) - 1] = 0; + + host_socket = std::make_unique< + boost::asio::local::stream_protocol::socket>( + conn.getIoService()); + host_socket->async_connect(ep, connectHandler); + } + }) + .onclose( + [](crow::websocket::Connection& conn, const std::string& reason) { + sessions.erase(&conn); + if (sessions.empty()) + { + host_socket = nullptr; + inputBuffer.clear(); + inputBuffer.shrink_to_fit(); + } + }) + .onmessage([](crow::websocket::Connection& conn, + const std::string& data, bool is_binary) { + inputBuffer += data; + doWrite(); + }); +} +} // namespace obmc_console +} // namespace crow |