Add support for multiple consoles
This drop adds support for multiple consoles. The following changes are
made to achieve this.
- Kept the "/console0" route for backward compatibility
- Added a new route "/console/<str>" to support multiple consoles. All
new consoles must use this route string.
Testing:
- Make sure that old console path /console0 is working.
[INFO "http_connection.hpp":209] Request: 0x1bc2e60 HTTP/1.1
GET /console0 ::ffff:x.x.xx.xxx
[DEBUG "routing.hpp":1240] Matched rule (upgrade) '/console0' 1 / 2
[DEBUG "obmc_console.hpp":212] Connection 0x1bdb67c opened
[DEBUG "obmc_console.hpp":241] Console Object path =
/xyz/openbmc_project/console/default service =
xyz.openbmc_project.Console.default Request target = /console0
[DEBUG "obmc_console.hpp":198] Console web socket path: /console0
Console unix FD: 12 duped FD: 13
[DEBUG "obmc_console.hpp":82] Reading from socket
[DEBUG "obmc_console.hpp":162] Remove connection 0x1bdb67c from
obmc console
- Make sure that new path for default console working
[INFO "http_connection.hpp":209] Request: 0x1bd76a8 HTTP/1.1
GET /console/default ::ffff:x.x.xx.xxx
[DEBUG "routing.hpp":1240] Matched rule (upgrade) '/console/<str>'
1 / 2
[DEBUG "obmc_console.hpp":212] Connection 0x1baf82c opened
[DEBUG "obmc_console.hpp":241] Console Object path =
/xyz/openbmc_project/console/default service =
xyz.openbmc_project.Console.default Request
target = /console/default
[DEBUG "obmc_console.hpp":198] Console web socket path:
/console/default Console unix FD: 12 duped FD: 13
[DEBUG "obmc_console.hpp":82] Reading from socket
[INFO "obmc_console.hpp":154] Closing websocket. Reason:
[DEBUG "obmc_console.hpp":162] Remove connection 0x1baf82c from
obmc console
- Make sure that path for hypervisor console is working.
[INFO "http_connection.hpp":209] Request: 0x1bc2e60 HTTP/1.1
GET /console/hypervisor ::ffff:x.x.xx.xxx
[DEBUG "routing.hpp":1240] Matched rule (upgrade) '/console/<str>'
1 / 2
[DEBUG "obmc_console.hpp":212] Connection 0x1bc5234 opened
[DEBUG "obmc_console.hpp":241] Console Object path =
/xyz/openbmc_project/console/hypervisor service =
xyz.openbmc_project.Console.hypervisor Request
target = /console/hypervisor
[DEBUG "obmc_console.hpp":198] Console web socket path:
/console/hypervisor Console unix FD: 12 duped FD: 13
[DEBUG "obmc_console.hpp":82] Reading from socket
[INFO "obmc_console.hpp":154] Closing websocket. Reason:
[DEBUG "obmc_console.hpp":162] Remove connection 0x1bc5234 from
obmc console
- Make sure that bad console path is failing properly due to DBUS error.
[INFO "http_connection.hpp":209] Request: 0x1bd76a8 HTTP/1.1
GET /console/badconsoleid ::ffff:x.x.xx.xxx
[DEBUG "routing.hpp":1240] Matched rule (upgrade) '/console/<str>'
1 / 2
[DEBUG "obmc_console.hpp":212] Connection 0x1bdb67c opened
[DEBUG "obmc_console.hpp":241] Console Object path =
/xyz/openbmc_project/console/badconsoleid service =
xyz.openbmc_project.Console.badconsoleid Request
target = /console/badconsoleid
[ERROR "obmc_console.hpp":174] Failed to call console Connect()
method DBUS error: No route to host
Change-Id: I9b617bc51e3ddc605dd7f4d213c805d05d2cfead
Signed-off-by: Ninad Palsule <ninad@linux.ibm.com>
Signed-off-by: Ed Tanous <edtanous@google.com>
diff --git a/http/routing.hpp b/http/routing.hpp
index ac1c310..f3bcfbb 100644
--- a/http/routing.hpp
+++ b/http/routing.hpp
@@ -305,7 +305,7 @@
crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>
myConnection = std::make_shared<
crow::websocket::ConnectionImpl<boost::asio::ip::tcp::socket>>(
- req, std::move(adaptor), openHandler, messageHandler,
+ req, req.url(), std::move(adaptor), openHandler, messageHandler,
messageExHandler, closeHandler, errorHandler);
myConnection->start();
}
@@ -320,7 +320,7 @@
boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>
myConnection = std::make_shared<crow::websocket::ConnectionImpl<
boost::beast::ssl_stream<boost::asio::ip::tcp::socket>>>(
- req, std::move(adaptor), openHandler, messageHandler,
+ req, req.url(), std::move(adaptor), openHandler, messageHandler,
messageExHandler, closeHandler, errorHandler);
myConnection->start();
}
diff --git a/http/websocket.hpp b/http/websocket.hpp
index e61f58b..0faa8c6 100644
--- a/http/websocket.hpp
+++ b/http/websocket.hpp
@@ -45,7 +45,7 @@
virtual void resumeRead() = 0;
virtual boost::asio::io_context& getIoContext() = 0;
virtual ~Connection() = default;
-
+ virtual boost::urls::url_view url() = 0;
boost::beast::http::request<boost::beast::http::string_body> req;
};
@@ -54,8 +54,8 @@
{
public:
ConnectionImpl(
- const crow::Request& reqIn, Adaptor adaptorIn,
- std::function<void(Connection&)> openHandlerIn,
+ const crow::Request& reqIn, boost::urls::url_view urlViewIn,
+ Adaptor adaptorIn, std::function<void(Connection&)> openHandlerIn,
std::function<void(Connection&, const std::string&, bool)>
messageHandlerIn,
std::function<void(crow::websocket::Connection&, std::string_view,
@@ -65,7 +65,7 @@
std::function<void(Connection&, const std::string&)> closeHandlerIn,
std::function<void(Connection&)> errorHandlerIn) :
Connection(reqIn),
- ws(std::move(adaptorIn)), inBuffer(inString, 131088),
+ uri(urlViewIn), ws(std::move(adaptorIn)), inBuffer(inString, 131088),
openHandler(std::move(openHandlerIn)),
messageHandler(std::move(messageHandlerIn)),
messageExHandler(std::move(messageExHandlerIn)),
@@ -215,6 +215,11 @@
});
}
+ boost::urls::url_view url() override
+ {
+ return uri;
+ }
+
void acceptDone()
{
BMCWEB_LOG_DEBUG << "Websocket accepted connection";
@@ -338,6 +343,8 @@
doRead();
}
+ boost::urls::url uri;
+
boost::beast::websocket::stream<Adaptor, false> ws;
bool readingDefered = false;
diff --git a/include/obmc_console.hpp b/include/obmc_console.hpp
index 7eaa153..f9b978d 100644
--- a/include/obmc_console.hpp
+++ b/include/obmc_console.hpp
@@ -185,8 +185,7 @@
auto iter = getConsoleHandlerMap().find(&conn);
if (iter == getConsoleHandlerMap().end())
{
- BMCWEB_LOG_ERROR << "Failed to find the handler";
- conn.close("Internal error");
+ BMCWEB_LOG_ERROR << "Connection was already closed";
return;
}
@@ -199,8 +198,7 @@
return;
}
- BMCWEB_LOG_DEBUG << "Console web socket path: " << conn.req.target()
- << " Console unix FD: " << unixfd << " duped FD: " << fd;
+ BMCWEB_LOG_DEBUG << "Console unix FD: " << unixfd << " duped FD: " << fd;
if (!iter->second->connect(fd))
{
@@ -209,10 +207,56 @@
}
}
+inline void
+ processConsoleObject(crow::websocket::Connection& conn,
+ const std::string& consoleObjPath,
+ const boost::system::error_code& ec,
+ const ::dbus::utility::MapperGetObject& objInfo)
+{
+ // Look up the handler
+ auto iter = getConsoleHandlerMap().find(&conn);
+ if (iter == getConsoleHandlerMap().end())
+ {
+ BMCWEB_LOG_ERROR << "Connection was already closed";
+ return;
+ }
+
+ if (ec)
+ {
+ BMCWEB_LOG_WARNING << "getDbusObject() for consoles failed. DBUS error:"
+ << ec.message();
+ conn.close("getDbusObject() for consoles failed.");
+ return;
+ }
+
+ const auto valueIface = objInfo.begin();
+ if (valueIface == objInfo.end())
+ {
+ BMCWEB_LOG_WARNING << "getDbusObject() returned unexpected size: "
+ << objInfo.size();
+ conn.close("getDbusObject() returned unexpected size");
+ return;
+ }
+
+ const std::string& consoleService = valueIface->first;
+ BMCWEB_LOG_DEBUG << "Looking up unixFD for Service " << consoleService
+ << " Path " << consoleObjPath;
+ // Call Connect() method to get the unix FD
+ crow::connections::systemBus->async_method_call(
+ [&conn](const boost::system::error_code& ec1,
+ const sdbusplus::message::unix_fd& unixfd) {
+ connectConsoleSocket(conn, ec1, unixfd);
+ },
+ consoleService, consoleObjPath, "xyz.openbmc_project.Console.Access",
+ "Connect");
+}
+
// Query consoles from DBUS and find the matching to the
// rules string.
inline void onOpen(crow::websocket::Connection& conn)
{
+ std::string consoleLeaf;
+
BMCWEB_LOG_DEBUG << "Connection " << &conn << " opened";
if (getConsoleHandlerMap().size() >= maxSessions)
@@ -227,23 +271,34 @@
conn.deferRead();
- // The console id 'default' is used for the console0
- // We need to change it when we provide full multi-console support.
- const std::string consolePath = "/xyz/openbmc_project/console/default";
- const std::string consoleService = "xyz.openbmc_project.Console.default";
+ // Keep old path for backward compatibility
+ if (conn.url().path() == "/console0")
+ {
+ consoleLeaf = "default";
+ }
+ else
+ {
+ // Get the console id from console router path and prepare the console
+ // object path and console service.
+ consoleLeaf = conn.url().segments().back();
+ }
+ std::string consolePath =
+ sdbusplus::message::object_path("/xyz/openbmc_project/console") /
+ consoleLeaf;
BMCWEB_LOG_DEBUG << "Console Object path = " << consolePath
- << " service = " << consoleService
- << " Request target = " << conn.req.target();
+ << " Request target = " << conn.url().path();
- // Call Connect() method to get the unix FD
- crow::connections::systemBus->async_method_call(
- [&conn](const boost::system::error_code& ec,
- const sdbusplus::message::unix_fd& unixfd) {
- connectConsoleSocket(conn, ec, unixfd);
- },
- consoleService, consolePath, "xyz.openbmc_project.Console.Access",
- "Connect");
+ // mapper call lambda
+ constexpr std::array<std::string_view, 1> interfaces = {
+ "xyz.openbmc_project.Console.Access"};
+
+ dbus::utility::getDbusObject(
+ consolePath, interfaces,
+ [&conn, consolePath](const boost::system::error_code& ec,
+ const ::dbus::utility::MapperGetObject& objInfo) {
+ processConsoleObject(conn, consolePath, ec, objInfo);
+ });
}
inline void onMessage(crow::websocket::Connection& conn,
@@ -267,6 +322,13 @@
.onopen(onOpen)
.onclose(onClose)
.onmessage(onMessage);
+
+ BMCWEB_ROUTE(app, "/console/<str>")
+ .privileges({{"OpenBMCHostConsole"}})
+ .websocket()
+ .onopen(onOpen)
+ .onclose(onClose)
+ .onmessage(onMessage);
}
} // namespace obmc_console
} // namespace crow