当前位置: 首页 > 知识库问答 >
问题:

c++ - bmcweb的console如何设置成多路sol??

艾成益
2024-08-23

bmcweb里面obmc_console.hpp的源码如下所示:

#pragma once
#### include "app.hpp"
#include "async_resp.hpp"
#include "websocket.hpp"
#include <sys/socket.h>
 
#include <boost/asio/local/stream_protocol.hpp>
#include <boost/container/flat_map.hpp>
#include <boost/system/error_code.hpp>
 
#include <array>
#include <memory>
#include <string>
#include <string_view>
 
namespace crow
{
namespace obmc_console
{
 
// Update this value each time we add new console route.
static constexpr const uint maxSessions = 32;
 
class ConsoleHandler : public std::enable_shared_from_this<ConsoleHandler>
{
  public:
    ConsoleHandler(boost::asio::io_context& ioc,
                   crow::websocket::Connection& connIn) :
        hostSocket(ioc), conn(connIn)
    {}
 
    ~ConsoleHandler() = default;
 
    ConsoleHandler(const ConsoleHandler&) = delete;
    ConsoleHandler(ConsoleHandler&&) = delete;
    ConsoleHandler& operator=(const ConsoleHandler&) = delete;
    ConsoleHandler& operator=(ConsoleHandler&&) = delete;
 
    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;
        hostSocket.async_write_some(
            boost::asio::buffer(inputBuffer.data(), inputBuffer.size()),
            [weak(weak_from_this())](const boost::beast::error_code& ec,
                                     std::size_t bytesWritten) {
                std::shared_ptr<ConsoleHandler> self = weak.lock();
                if (self == nullptr)
                {
                    return;
                }
 
                self->doingWrite = false;
                self->inputBuffer.erase(0, bytesWritten);
                if (ec == boost::asio::error::eof)
                {
                    self->conn.close("Error in reading to host port");
                    return;
                }
                if (ec)
                {
                    BMCWEB_LOG_ERROR("Error in host serial write {}",
                                     ec.message());
                    return;
                }
                self->doWrite();
            });
    }
 
    static void afterSendEx(const std::weak_ptr<ConsoleHandler>& weak)
    {
        std::shared_ptr<ConsoleHandler> self = weak.lock();
        if (self == nullptr)
        {
            return;
        }
        self->doRead();
    }
 
    void doRead()
    {
        BMCWEB_LOG_DEBUG("Reading from socket");
        hostSocket.async_read_some(
            boost::asio::buffer(outputBuffer),
            [this, weakSelf(weak_from_this())](
                const boost::system::error_code& ec, std::size_t bytesRead) {
                BMCWEB_LOG_DEBUG("read done.  Read {} bytes", bytesRead);
                std::shared_ptr<ConsoleHandler> self = weakSelf.lock();
                if (self == nullptr)
                {
                    return;
                }
                if (ec)
                {
                    BMCWEB_LOG_ERROR("Couldn't read from host serial port: {}",
                                     ec.message());
                    conn.close("Error connecting to host port");
                    return;
                }
                std::string_view payload(outputBuffer.data(), bytesRead);
                self->conn.sendEx(
                    crow::websocket::MessageType::Binary, payload,
                    std::bind_front(afterSendEx, weak_from_this()));
            });
    }
 
    bool connect(int fd)
    {
        boost::system::error_code ec;
        boost::asio::local::stream_protocol proto;
        hostSocket.assign(proto, fd, ec);
        if (ec)
        {
            BMCWEB_LOG_ERROR(
                "Failed to assign the DBUS socket Socket assign error: {}",
                ec.message());
            return false;
        }
        conn.resumeRead();
        doWrite();
        doRead();
        return true;
    }
 
    boost::asio::local::stream_protocol::socket hostSocket;
    std::array<char, 4096> outputBuffer{};
    std::string inputBuffer;
    bool doingWrite = false;
    crow::websocket::Connection& conn;
};
 
using ObmcConsoleMap = boost::container::flat_map<
    crow::websocket::Connection*, std::shared_ptr<ConsoleHandler>, std::less<>,
    std::vector<std::pair<crow::websocket::Connection*,
                          std::shared_ptr<ConsoleHandler>>>>;
 
inline ObmcConsoleMap& getConsoleHandlerMap()
{
    static ObmcConsoleMap map;
    return map;
}
 
// Remove connection from the connection map and if connection map is empty
// then remove the handler from handlers map.
inline void onClose(crow::websocket::Connection& conn, const std::string& err)
{
    BMCWEB_LOG_INFO("Closing websocket. Reason: {}", err);
    auto iter = getConsoleHandlerMap().find(&conn);
    if (iter == getConsoleHandlerMap().end())
    {
        BMCWEB_LOG_CRITICAL("Unable to find connection {}", logPtr(&conn));
        return;
    }
    BMCWEB_LOG_DEBUG("Remove connection {} from obmc console", logPtr(&conn));
    // Removed last connection so remove the path
    getConsoleHandlerMap().erase(iter);
}
 
inline void connectConsoleSocket(crow::websocket::Connection& conn,
                                 const boost::system::error_code& ec,
                                 const sdbusplus::message::unix_fd& unixfd)
{
    if (ec)
    {
        BMCWEB_LOG_ERROR(
            "Failed to call console Connect() method DBUS error: {}",
            ec.message());
        conn.close("Failed to connect");
        return;
    }
 
    // Look up the handler
    auto iter = getConsoleHandlerMap().find(&conn);
    if (iter == getConsoleHandlerMap().end())
    {
        BMCWEB_LOG_ERROR("Connection was already closed");
        return;
    }
 
    int fd = dup(unixfd);
    if (fd == -1)
    {
        BMCWEB_LOG_ERROR("Failed to dup the DBUS unixfd error: {}",
                         strerror(errno));
        conn.close("Internal error");
        return;
    }
    BMCWEB_LOG_DEBUG("Console duped FD: {}", fd);
    if (!iter->second->connect(fd))
    {
        close(fd);
        conn.close("Internal Error");
    }
}
 
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 {} Path {}", consoleService,
                     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 {} opened", logPtr(&conn));
    if (getConsoleHandlerMap().size() >= maxSessions)
    {
        conn.close("Max sessions are already connected");
        return;
    }
 
    std::shared_ptr<ConsoleHandler> handler =
        std::make_shared<ConsoleHandler>(conn.getIoContext(), conn);
    getConsoleHandlerMap().emplace(&conn, handler);
 
    conn.deferRead();
 
    // 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 = {} Request target = {}",
                     consolePath, conn.url().path());
 
    // 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,
                      const std::string& data, bool /*isBinary*/)
{
    auto handler = getConsoleHandlerMap().find(&conn);
    if (handler == getConsoleHandlerMap().end())
    {
        BMCWEB_LOG_CRITICAL("Unable to find connection {}", logPtr(&conn));
        return;
    }
    handler->second->inputBuffer += data;
    handler->second->doWrite();
}
 
inline void requestRoutes(App& app)
{
    BMCWEB_ROUTE(app, "/console0")
        .privileges({{"OpenBMCHostConsole"}})
        .websocket()
        .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

如果bmcweb和多路主机相连,即需要操作多个console,openbmc该如何配置对应的uart,保证不同的路由 BMCWEB_ROUTE(app, "/console/")选择不同的uart连接到指定的主机

共有1个答案

靳祺然
2024-08-23

在OpenBMC环境中,当bmcweb需要支持多个控制台(console)会话,每个会话连接到不同的UART端口或不同的主机时,通常需要通过几个步骤来配置和扩展当前的系统。这里的关键在于如何根据请求的路由动态地连接到正确的UART端口或主机。

步骤 1: 扩展 DBUS 接口

首先,你需要确保你的DBUS服务(通常是在你的BMC固件中实现的)能够根据不同的请求(如基于路径的/console/serial0/console/serial1等)返回正确的UART端口或主机连接信息。这可能需要你修改或扩展现有的DBUS服务来支持更多的连接选项。

步骤 2: 修改 bmcweb 路由处理

在你的bmcweb代码中,你已经有了基本的路由处理(如/console0/console/<str>)。你需要扩展这些路由,以便它们可以根据URL的<str>部分来决定连接到哪个UART或主机。

  • 修改onOpen函数:确保在建立WebSocket连接时,根据请求的URL(如/console/serial0)来查询正确的DBUS服务并获取相应的连接信息(如UNIX文件描述符)。
  • 更新processConsoleObjectconnectConsoleSocket函数:这些函数需要根据从DBUS服务获取的信息来设置正确的UART连接。你可能需要传递额外的参数(如UART端口名称或主机ID)给这些函数。

步骤 3: 映射 URL 到 UART/主机

在你的BMC固件或配置中,你需要有一个方法来映射URL的<str>部分到具体的UART端口或主机。这可以通过配置文件、环境变量或直接在DBUS服务中硬编码来实现。

步骤 4: 测试和验证

完成上述更改后,你需要进行彻底的测试来确保:

  • 每个控制台路由都能正确连接到其指定的UART端口或主机。
  • 并发连接(如果支持)不会相互干扰。
  • 安全性(如权限检查)仍然得到妥善处理。

示例代码扩展

以下是一个简化的例子,展示了如何在onOpen函数中根据URL动态选择UART端口:

void onOpen(crow::websocket::Connection& conn)
{
    // ... (省略了之前的代码)

    std::string consoleLeaf = conn.url().segments().back();
    std::string uartPort = getUartPortFromConsoleLeaf(consoleLeaf); // 假设这个函数根据consoleLeaf返回UART端口名

    // 使用uartPort来配置你的连接
    // ... (省略了实际的连接配置代码)
}

std::string getUartPortFromConsoleLeaf(const std::string& leaf)
{
    // 这里应该有逻辑来根据leaf(如"serial0", "serial1"等)返回正确的UART端口名
    // 示例:硬编码返回
    if (leaf == "serial0")
    {
        return "/dev/ttyS0";
    }
    else if (leaf == "serial1")
    {
        return "/dev/ttyS1";
    }
    // ... (更多条件分支)
    return ""; // 默认或错误情况
}

注意:这个例子假设你有一个函数getUartPortFromConsoleLeaf,它根据consoleLeaf的值返回UART端口名。在实际应用中,这个函数可能需要与DBUS服务交互来获取端口名。

 类似资料:
  • 生成Mandelbrot分形的方程是。问题是,在计算机程序中,C用于屏幕上的缩放/分辨率和位置。我的问题是,我怎样才能得到这样的分形: 沃尔夫拉姆 () 我的代码(来自罗塞塔代码):

  • 使用指南 - 统计设置 - 转化设置 - 转化路径如何设置 在设置页面转化时,选择开启转化路径。 点击开启后界面如图所示: 若到达目标页面需要多个步骤,可点击①位置增加步骤。 若该步骤可以有多个页面,经过其中任意一个页面都算作经过该步骤,可以点击②位置增添该步骤的网址,网址内容也支持使用通配符。 系统默认只要到达目标页面都会计为转化。若我们认为必须经过某个步骤后到达目标页面的行为才可以计为转化,可

  • 问题内容: 我想从而不是从站点根目录开始导航。 例如 当我的网址是 我该怎么办? 问题答案: 当用户导航到时,将基本标记添加到正在提供的主HTML文件中: 可以在开发人员指南/使用$ location中 找到更多信息。

  • 我创建laravel项目,在我的本地Windows电脑上工作良好。一旦我上传到Centos7服务器(通过SSH),我的所有路由都不工作。我试图通过行动修复,例如清除缓存,删除供应商文件夹和重做安装作曲家,但没有任何帮助。我从下面的日志中抓取。 [2018-12-20 13:09:17]本地的。错误:逻辑异常:无法为序列化准备路由[api/user]。在 /var/www/html/srp/vend

  • 本文向大家介绍如何在Windows中的Eclipse上设置C / C ++?,包括了如何在Windows中的Eclipse上设置C / C ++?的使用技巧和注意事项,需要的朋友参考一下 步骤1-安装MinGW GCC或Cygwin GCC 要将Eclipse用于C / C ++编程,您需要一个C / C ++编译器。在Windows上,您可以安装MinGW GCC或Cygwin GCC。如果不确

  • 我正在使用spring boot编写一个api,我希望将所有资源映射到一个公共基本路径(在本例中为/api)后面。但是,我不想为每个RestController类做注解(例如,通过使用@RequestMapping对其进行注解)。我想过以下解决方案,但它们都有我不想要的缺点: 创建一个基类(如ApiRestController)并让所有其他RestController继承这个类。这有一个缺点,即类

  • 下面是我的本地主机目录结构- htdocs\software\office_admin\exam_excel\Classes\PHPExcel.php htdocs\software\office_admin\ exam_excel\Classes\PHPExcel\IOFactory.php htdocs\software\office_admin\include\ classes\myownc

  • 我的源代码位于如下文件夹结构中: MyGitroot/MyProj/Module1/SRC/Main/Groovy/COM/MyPak/Module1 MyGitroot/MyProj/Module2/SRC/Main/Groovy/COM/MyPak/Module2 MyGitroot/MyProj/Module3/SRC/Main/Groovy/COM/MyPak/Module3 Gradle