summaryrefslogtreecommitdiff
path: root/include/obmc_console.hpp
diff options
context:
space:
mode:
authorEd Tanous <ed.tanous@intel.com>2018-08-23 21:27:23 +0300
committerEd Tanous <ed.tanous@intel.com>2018-09-05 18:51:19 +0300
commit2daf6725629dc9fae0ba554ddef0577fee60ef09 (patch)
tree26c2c8c4965a039af2e811bff09e4eca218308b6 /include/obmc_console.hpp
parent1abe55ef9844afcddcab9d862ae06118f3a2390c (diff)
downloadbmcweb-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.hpp157
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