1 #include "sol_manager.hpp" 2 3 #include "main.hpp" 4 #include "sol_context.hpp" 5 6 #include <sys/socket.h> 7 #include <sys/un.h> 8 9 #include <boost/asio/basic_stream_socket.hpp> 10 #include <boost/asio/io_context.hpp> 11 #include <boost/asio/local/stream_protocol.hpp> 12 #include <boost/asio/write.hpp> 13 #include <ipmid/utils.hpp> 14 #include <phosphor-logging/lg2.hpp> 15 #include <sdbusplus/message/types.hpp> 16 17 #include <chrono> 18 #include <cmath> 19 20 constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL"; 21 constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/"; 22 constexpr const char* PROP_INTF = "org.freedesktop.DBus.Properties"; 23 24 namespace sol 25 { 26 27 std::unique_ptr<sdbusplus::bus::match_t> matchPtrSOL(nullptr); 28 std::unique_ptr<sdbusplus::bus::match_t> solConfPropertiesSignal(nullptr); 29 30 void Manager::initConsoleSocket() 31 { 32 // explicit length constructor for NUL-prefixed abstract path 33 std::string path(CONSOLE_SOCKET_PATH, CONSOLE_SOCKET_PATH_LEN); 34 boost::asio::local::stream_protocol::endpoint ep(path); 35 consoleSocket = 36 std::make_unique<boost::asio::local::stream_protocol::socket>(*io); 37 consoleSocket->connect(ep); 38 } 39 40 void Manager::consoleInputHandler() 41 { 42 boost::system::error_code ec; 43 boost::asio::socket_base::bytes_readable cmd(true); 44 consoleSocket->io_control(cmd, ec); 45 size_t readSize; 46 if (!ec) 47 { 48 readSize = cmd.get(); 49 } 50 else 51 { 52 lg2::error( 53 "Reading ready count from host console socket failed: {ERROR}", 54 "ERROR", ec.value()); 55 return; 56 } 57 std::vector<uint8_t> buffer(readSize); 58 ec.clear(); 59 size_t readDataLen = 60 consoleSocket->read_some(boost::asio::buffer(buffer), ec); 61 if (ec) 62 { 63 lg2::error("Reading from host console socket failed: {ERROR}", "ERROR", 64 ec.value()); 65 return; 66 } 67 68 // Update the Console buffer with data read from the socket 69 buffer.resize(readDataLen); 70 dataBuffer.write(buffer); 71 } 72 73 int Manager::writeConsoleSocket(const std::vector<uint8_t>& input) const 74 { 75 boost::system::error_code ec; 76 boost::asio::write(*consoleSocket, boost::asio::buffer(input), ec); 77 return ec.value(); 78 } 79 80 void Manager::startHostConsole() 81 { 82 if (!consoleSocket) 83 { 84 initConsoleSocket(); 85 } 86 87 // Register callback to close SOL session for disable SSH SOL 88 if (matchPtrSOL == nullptr) 89 { 90 registerSOLServiceChangeCallback(); 91 } 92 93 consoleSocket->async_wait(boost::asio::socket_base::wait_read, 94 [this](const boost::system::error_code& ec) { 95 if (!ec) 96 { 97 consoleInputHandler(); 98 startHostConsole(); 99 } 100 }); 101 } // namespace sol 102 103 void Manager::stopHostConsole() 104 { 105 if (consoleSocket) 106 { 107 consoleSocket->cancel(); 108 consoleSocket.reset(); 109 } 110 } 111 112 void Manager::updateSOLParameter(uint8_t channelNum) 113 { 114 std::variant<uint8_t, bool> value; 115 sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection()); 116 static std::string solService{}; 117 ipmi::PropertyMap properties; 118 std::string ethdevice = ipmi::getChannelName(channelNum); 119 std::string solPathWitheEthName = solPath + ethdevice; 120 if (solService.empty()) 121 { 122 try 123 { 124 solService = 125 ipmi::getService(dbus, solInterface, solPathWitheEthName); 126 } 127 catch (const std::runtime_error& e) 128 { 129 solService.clear(); 130 lg2::error("Get SOL service failed: {ERROR}", "ERROR", e); 131 return; 132 } 133 } 134 try 135 { 136 properties = ipmi::getAllDbusProperties( 137 dbus, solService, solPathWitheEthName, solInterface); 138 } 139 catch (const std::runtime_error& e) 140 { 141 lg2::error("Setting sol parameter: {ERROR}", "ERROR", e); 142 return; 143 } 144 145 progress = std::get<uint8_t>(properties["Progress"]); 146 147 enable = std::get<bool>(properties["Enable"]); 148 149 forceEncrypt = std::get<bool>(properties["ForceEncryption"]); 150 151 forceAuth = std::get<bool>(properties["ForceAuthentication"]); 152 153 solMinPrivilege = static_cast<session::Privilege>( 154 std::get<uint8_t>(properties["Privilege"])); 155 156 accumulateInterval = 157 std::get<uint8_t>((properties["AccumulateIntervalMS"])) * 158 sol::accIntervalFactor * 1ms; 159 160 sendThreshold = std::get<uint8_t>(properties["Threshold"]); 161 162 retryCount = std::get<uint8_t>(properties["RetryCount"]); 163 164 retryInterval = std::get<uint8_t>(properties["RetryIntervalMS"]) * 165 sol::retryIntervalFactor * 1ms; 166 167 return; 168 } 169 170 void Manager::startPayloadInstance(uint8_t payloadInstance, 171 session::SessionID sessionID) 172 { 173 if (payloadMap.empty()) 174 { 175 try 176 { 177 startHostConsole(); 178 } 179 catch (const std::exception& e) 180 { 181 lg2::error( 182 "Encountered exception when starting host console. Hence stopping host console: {ERROR}", 183 "ERROR", e); 184 stopHostConsole(); 185 throw; 186 } 187 } 188 189 // Create the SOL Context data for payload instance 190 std::shared_ptr<Context> context = Context::makeContext( 191 io, retryCount, sendThreshold, payloadInstance, sessionID); 192 193 payloadMap.emplace(payloadInstance, std::move(context)); 194 } 195 196 void Manager::stopPayloadInstance(uint8_t payloadInstance) 197 { 198 auto iter = payloadMap.find(payloadInstance); 199 if (iter == payloadMap.end()) 200 { 201 throw std::runtime_error("SOL Payload instance not found "); 202 } 203 204 payloadMap.erase(iter); 205 206 if (payloadMap.empty()) 207 { 208 stopHostConsole(); 209 210 dataBuffer.erase(dataBuffer.size()); 211 } 212 } 213 214 void Manager::stopAllPayloadInstance() 215 { 216 // Erase all payload instance 217 payloadMap.erase(payloadMap.begin(), payloadMap.end()); 218 219 stopHostConsole(); 220 221 dataBuffer.erase(dataBuffer.size()); 222 } 223 224 void registerSOLServiceChangeCallback() 225 { 226 using namespace sdbusplus::bus::match::rules; 227 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 228 try 229 { 230 auto servicePath = ipmi::getDbusObject( 231 bus, "xyz.openbmc_project.Control.Service.Attributes", 232 "/xyz/openbmc_project/control/service", "obmc_2dconsole"); 233 234 if (!std::empty(servicePath.first)) 235 { 236 matchPtrSOL = std::make_unique<sdbusplus::bus::match_t>( 237 bus, 238 path_namespace(servicePath.first) + 239 "arg0namespace='xyz.openbmc_project.Control.Service." 240 "Attributes'" 241 ", " + 242 type::signal() + member("PropertiesChanged") + 243 interface("org.freedesktop.DBus.Properties"), 244 [](sdbusplus::message_t& msg) { 245 std::string intfName; 246 std::map<std::string, std::variant<bool>> properties; 247 msg.read(intfName, properties); 248 249 const auto it = properties.find("Enabled"); 250 if (it != properties.end()) 251 { 252 const bool* state = std::get_if<bool>(&it->second); 253 254 if (state != nullptr && *state == false) 255 { 256 // Stop all the payload session. 257 sol::Manager::get().stopAllPayloadInstance(); 258 } 259 } 260 }); 261 } 262 } 263 catch (const sdbusplus::exception_t& e) 264 { 265 lg2::error( 266 "Failed to get service path in registerSOLServiceChangeCallback: {ERROR}", 267 "ERROR", e); 268 } 269 } 270 271 void procSolConfChange(sdbusplus::message_t& msg) 272 { 273 using SolConfVariant = std::variant<bool, uint8_t>; 274 using SolConfProperties = 275 std::vector<std::pair<std::string, SolConfVariant>>; 276 277 std::string iface; 278 SolConfProperties properties; 279 280 try 281 { 282 msg.read(iface, properties); 283 } 284 catch (const std::exception& e) 285 { 286 lg2::error("procSolConfChange get properties FAIL: {ERROR}", "ERROR", 287 e); 288 return; 289 } 290 291 for (const auto& prop : properties) 292 { 293 if (prop.first == "Progress") 294 { 295 sol::Manager::get().progress = std::get<uint8_t>(prop.second); 296 } 297 else if (prop.first == "Enable") 298 { 299 sol::Manager::get().enable = std::get<bool>(prop.second); 300 } 301 else if (prop.first == "ForceEncryption") 302 { 303 sol::Manager::get().forceEncrypt = std::get<bool>(prop.second); 304 } 305 else if (prop.first == "ForceAuthentication") 306 { 307 sol::Manager::get().forceAuth = std::get<bool>(prop.second); 308 } 309 else if (prop.first == "Privilege") 310 { 311 sol::Manager::get().solMinPrivilege = 312 static_cast<session::Privilege>(std::get<uint8_t>(prop.second)); 313 } 314 else if (prop.first == "AccumulateIntervalMS") 315 { 316 sol::Manager::get().accumulateInterval = 317 std::get<uint8_t>(prop.second) * sol::accIntervalFactor * 1ms; 318 } 319 else if (prop.first == "Threshold") 320 { 321 sol::Manager::get().sendThreshold = std::get<uint8_t>(prop.second); 322 } 323 else if (prop.first == "RetryCount") 324 { 325 sol::Manager::get().retryCount = std::get<uint8_t>(prop.second); 326 } 327 else if (prop.first == "RetryIntervalMS") 328 { 329 sol::Manager::get().retryInterval = 330 std::get<uint8_t>(prop.second) * sol::retryIntervalFactor * 1ms; 331 } 332 } 333 } 334 335 void registerSolConfChangeCallbackHandler(std::string channel) 336 { 337 if (solConfPropertiesSignal == nullptr) 338 { 339 using namespace sdbusplus::bus::match::rules; 340 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 341 try 342 { 343 auto servicePath = solPath + channel; 344 345 solConfPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>( 346 bus, propertiesChangedNamespace(servicePath, solInterface), 347 procSolConfChange); 348 } 349 catch (const sdbusplus::exception_t& e) 350 { 351 lg2::error( 352 "Failed to get service path in registerSolConfChangeCallbackHandler, channel: {CHANNEL}, error: {ERROR}", 353 "CHANNEL", channel, "ERROR", e); 354 } 355 } 356 return; 357 } 358 359 } // namespace sol 360