#include "sol_manager.hpp" #include "main.hpp" #include "sol_context.hpp" #include #include #include #include #include #include #include #include #include #include #include constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL"; constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/"; constexpr const char* PROP_INTF = "org.freedesktop.DBus.Properties"; namespace sol { std::unique_ptr matchPtrSOL(nullptr); std::unique_ptr solConfPropertiesSignal(nullptr); void Manager::initConsoleSocket() { // explicit length constructor for NUL-prefixed abstract path std::string path(CONSOLE_SOCKET_PATH, CONSOLE_SOCKET_PATH_LEN); boost::asio::local::stream_protocol::endpoint ep(path); consoleSocket = std::make_unique(*io); consoleSocket->connect(ep); } void Manager::consoleInputHandler() { boost::system::error_code ec; boost::asio::socket_base::bytes_readable cmd(true); consoleSocket->io_control(cmd, ec); size_t readSize; if (!ec) { readSize = cmd.get(); } else { lg2::error( "Reading ready count from host console socket failed: {ERROR}", "ERROR", ec.value()); return; } std::vector buffer(readSize); ec.clear(); size_t readDataLen = consoleSocket->read_some(boost::asio::buffer(buffer), ec); if (ec) { lg2::error("Reading from host console socket failed: {ERROR}", "ERROR", ec.value()); return; } // Update the Console buffer with data read from the socket buffer.resize(readDataLen); dataBuffer.write(buffer); } int Manager::writeConsoleSocket(const std::vector& input, bool breakFlag) const { boost::system::error_code ec; if (breakFlag) { consoleSocket->send(boost::asio::buffer(input), MSG_OOB, ec); } else { consoleSocket->send(boost::asio::buffer(input), 0, ec); } return ec.value(); } void Manager::startHostConsole() { if (!consoleSocket) { initConsoleSocket(); } // Register callback to close SOL session for disable SSH SOL if (matchPtrSOL == nullptr) { registerSOLServiceChangeCallback(); } consoleSocket->async_wait(boost::asio::socket_base::wait_read, [this](const boost::system::error_code& ec) { if (!ec) { consoleInputHandler(); startHostConsole(); } }); } // namespace sol void Manager::stopHostConsole() { if (consoleSocket) { consoleSocket->cancel(); consoleSocket.reset(); } } void Manager::updateSOLParameter(uint8_t channelNum) { std::variant value; sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection()); static std::string solService{}; ipmi::PropertyMap properties; std::string ethdevice = ipmi::getChannelName(channelNum); std::string solPathWitheEthName = solPath + ethdevice; if (solService.empty()) { try { solService = ipmi::getService(dbus, solInterface, solPathWitheEthName); } catch (const std::runtime_error& e) { solService.clear(); lg2::error("Get SOL service failed: {ERROR}", "ERROR", e); return; } } try { properties = ipmi::getAllDbusProperties( dbus, solService, solPathWitheEthName, solInterface); } catch (const std::runtime_error& e) { lg2::error("Setting sol parameter: {ERROR}", "ERROR", e); return; } progress = std::get(properties["Progress"]); enable = std::get(properties["Enable"]); forceEncrypt = std::get(properties["ForceEncryption"]); forceAuth = std::get(properties["ForceAuthentication"]); solMinPrivilege = static_cast( std::get(properties["Privilege"])); accumulateInterval = std::get((properties["AccumulateIntervalMS"])) * sol::accIntervalFactor * 1ms; sendThreshold = std::get(properties["Threshold"]); retryCount = std::get(properties["RetryCount"]); retryInterval = std::get(properties["RetryIntervalMS"]) * sol::retryIntervalFactor * 1ms; return; } void Manager::startPayloadInstance(uint8_t payloadInstance, session::SessionID sessionID) { if (payloadMap.empty()) { try { startHostConsole(); } catch (const std::exception& e) { lg2::error( "Encountered exception when starting host console. Hence stopping host console: {ERROR}", "ERROR", e); stopHostConsole(); throw; } } // Create the SOL Context data for payload instance std::shared_ptr context = Context::makeContext( io, retryCount, sendThreshold, payloadInstance, sessionID); payloadMap.emplace(payloadInstance, std::move(context)); } void Manager::stopPayloadInstance(uint8_t payloadInstance) { auto iter = payloadMap.find(payloadInstance); if (iter == payloadMap.end()) { throw std::runtime_error("SOL Payload instance not found "); } payloadMap.erase(iter); if (payloadMap.empty()) { stopHostConsole(); dataBuffer.erase(dataBuffer.size()); } } void Manager::stopAllPayloadInstance() { // Erase all payload instance payloadMap.erase(payloadMap.begin(), payloadMap.end()); stopHostConsole(); dataBuffer.erase(dataBuffer.size()); } void registerSOLServiceChangeCallback() { using namespace sdbusplus::bus::match::rules; sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; try { auto servicePath = ipmi::getDbusObject( bus, "xyz.openbmc_project.Control.Service.Attributes", "/xyz/openbmc_project/control/service", "_6fbmc_2dconsole"); if (!std::empty(servicePath.first)) { matchPtrSOL = std::make_unique( bus, path_namespace(servicePath.first) + "arg0namespace='xyz.openbmc_project.Control.Service." "Attributes'" ", " + type::signal() + member("PropertiesChanged") + interface("org.freedesktop.DBus.Properties"), [](sdbusplus::message_t& msg) { std::string intfName; std::map> properties; msg.read(intfName, properties); const auto it = properties.find("Enabled"); if (it != properties.end()) { const bool* state = std::get_if(&it->second); if (state != nullptr && *state == false) { // Stop all the payload session. sol::Manager::get().stopAllPayloadInstance(); } } }); } } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to get service path in registerSOLServiceChangeCallback: {ERROR}", "ERROR", e); } } void procSolConfChange(sdbusplus::message_t& msg) { using SolConfVariant = std::variant; using SolConfProperties = std::vector>; std::string iface; SolConfProperties properties; try { msg.read(iface, properties); } catch (const std::exception& e) { lg2::error("procSolConfChange get properties FAIL: {ERROR}", "ERROR", e); return; } for (const auto& prop : properties) { if (prop.first == "Progress") { sol::Manager::get().progress = std::get(prop.second); } else if (prop.first == "Enable") { sol::Manager::get().enable = std::get(prop.second); } else if (prop.first == "ForceEncryption") { sol::Manager::get().forceEncrypt = std::get(prop.second); } else if (prop.first == "ForceAuthentication") { sol::Manager::get().forceAuth = std::get(prop.second); } else if (prop.first == "Privilege") { sol::Manager::get().solMinPrivilege = static_cast(std::get(prop.second)); } else if (prop.first == "AccumulateIntervalMS") { sol::Manager::get().accumulateInterval = std::get(prop.second) * sol::accIntervalFactor * 1ms; } else if (prop.first == "Threshold") { sol::Manager::get().sendThreshold = std::get(prop.second); } else if (prop.first == "RetryCount") { sol::Manager::get().retryCount = std::get(prop.second); } else if (prop.first == "RetryIntervalMS") { sol::Manager::get().retryInterval = std::get(prop.second) * sol::retryIntervalFactor * 1ms; } } } void registerSolConfChangeCallbackHandler(std::string channel) { if (solConfPropertiesSignal == nullptr) { using namespace sdbusplus::bus::match::rules; sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; try { auto servicePath = solPath + channel; solConfPropertiesSignal = std::make_unique( bus, propertiesChangedNamespace(servicePath, solInterface), procSolConfChange); } catch (const sdbusplus::exception_t& e) { lg2::error( "Failed to get service path in registerSolConfChangeCallbackHandler, channel: {CHANNEL}, error: {ERROR}", "CHANNEL", channel, "ERROR", e); } } return; } } // namespace sol