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, 74 bool breakFlag) const 75 { 76 boost::system::error_code ec; 77 if (breakFlag) 78 { 79 consoleSocket->send(boost::asio::buffer(input), MSG_OOB, ec); 80 } 81 else 82 { 83 consoleSocket->send(boost::asio::buffer(input), 0, ec); 84 } 85 86 return ec.value(); 87 } 88 89 void Manager::startHostConsole() 90 { 91 if (!consoleSocket) 92 { 93 initConsoleSocket(); 94 } 95 96 // Register callback to close SOL session for disable SSH SOL 97 if (matchPtrSOL == nullptr) 98 { 99 registerSOLServiceChangeCallback(); 100 } 101 102 consoleSocket->async_wait(boost::asio::socket_base::wait_read, 103 [this](const boost::system::error_code& ec) { 104 if (!ec) 105 { 106 consoleInputHandler(); 107 startHostConsole(); 108 } 109 }); 110 } // namespace sol 111 112 void Manager::stopHostConsole() 113 { 114 if (consoleSocket) 115 { 116 consoleSocket->cancel(); 117 consoleSocket.reset(); 118 } 119 } 120 121 void Manager::updateSOLParameter(uint8_t channelNum) 122 { 123 std::variant<uint8_t, bool> value; 124 sdbusplus::bus_t dbus(ipmid_get_sd_bus_connection()); 125 static std::string solService{}; 126 ipmi::PropertyMap properties; 127 std::string ethdevice = ipmi::getChannelName(channelNum); 128 std::string solPathWitheEthName = solPath + ethdevice; 129 if (solService.empty()) 130 { 131 try 132 { 133 solService = 134 ipmi::getService(dbus, solInterface, solPathWitheEthName); 135 } 136 catch (const std::runtime_error& e) 137 { 138 solService.clear(); 139 lg2::error("Get SOL service failed: {ERROR}", "ERROR", e); 140 return; 141 } 142 } 143 try 144 { 145 properties = ipmi::getAllDbusProperties( 146 dbus, solService, solPathWitheEthName, solInterface); 147 } 148 catch (const std::runtime_error& e) 149 { 150 lg2::error("Setting sol parameter: {ERROR}", "ERROR", e); 151 return; 152 } 153 154 progress = std::get<uint8_t>(properties["Progress"]); 155 156 enable = std::get<bool>(properties["Enable"]); 157 158 forceEncrypt = std::get<bool>(properties["ForceEncryption"]); 159 160 forceAuth = std::get<bool>(properties["ForceAuthentication"]); 161 162 solMinPrivilege = static_cast<session::Privilege>( 163 std::get<uint8_t>(properties["Privilege"])); 164 165 accumulateInterval = 166 std::get<uint8_t>((properties["AccumulateIntervalMS"])) * 167 sol::accIntervalFactor * 1ms; 168 169 sendThreshold = std::get<uint8_t>(properties["Threshold"]); 170 171 retryCount = std::get<uint8_t>(properties["RetryCount"]); 172 173 retryInterval = std::get<uint8_t>(properties["RetryIntervalMS"]) * 174 sol::retryIntervalFactor * 1ms; 175 176 return; 177 } 178 179 void Manager::startPayloadInstance(uint8_t payloadInstance, 180 session::SessionID sessionID) 181 { 182 if (payloadMap.empty()) 183 { 184 try 185 { 186 startHostConsole(); 187 } 188 catch (const std::exception& e) 189 { 190 lg2::error( 191 "Encountered exception when starting host console. Hence stopping host console: {ERROR}", 192 "ERROR", e); 193 stopHostConsole(); 194 throw; 195 } 196 } 197 198 // Create the SOL Context data for payload instance 199 std::shared_ptr<Context> context = Context::makeContext( 200 io, retryCount, sendThreshold, payloadInstance, sessionID); 201 202 payloadMap.emplace(payloadInstance, std::move(context)); 203 } 204 205 void Manager::stopPayloadInstance(uint8_t payloadInstance) 206 { 207 auto iter = payloadMap.find(payloadInstance); 208 if (iter == payloadMap.end()) 209 { 210 throw std::runtime_error("SOL Payload instance not found "); 211 } 212 213 payloadMap.erase(iter); 214 215 if (payloadMap.empty()) 216 { 217 stopHostConsole(); 218 219 dataBuffer.erase(dataBuffer.size()); 220 } 221 } 222 223 void Manager::stopAllPayloadInstance() 224 { 225 // Erase all payload instance 226 payloadMap.erase(payloadMap.begin(), payloadMap.end()); 227 228 stopHostConsole(); 229 230 dataBuffer.erase(dataBuffer.size()); 231 } 232 233 void registerSOLServiceChangeCallback() 234 { 235 using namespace sdbusplus::bus::match::rules; 236 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 237 try 238 { 239 auto servicePath = ipmi::getDbusObject( 240 bus, "xyz.openbmc_project.Control.Service.Attributes", 241 "/xyz/openbmc_project/control/service", "_6fbmc_2dconsole"); 242 243 if (!std::empty(servicePath.first)) 244 { 245 matchPtrSOL = std::make_unique<sdbusplus::bus::match_t>( 246 bus, 247 path_namespace(servicePath.first) + 248 "arg0namespace='xyz.openbmc_project.Control.Service." 249 "Attributes'" 250 ", " + 251 type::signal() + member("PropertiesChanged") + 252 interface("org.freedesktop.DBus.Properties"), 253 [](sdbusplus::message_t& msg) { 254 std::string intfName; 255 std::map<std::string, std::variant<bool>> properties; 256 msg.read(intfName, properties); 257 258 const auto it = properties.find("Enabled"); 259 if (it != properties.end()) 260 { 261 const bool* state = std::get_if<bool>(&it->second); 262 263 if (state != nullptr && *state == false) 264 { 265 // Stop all the payload session. 266 sol::Manager::get().stopAllPayloadInstance(); 267 } 268 } 269 }); 270 } 271 } 272 catch (const sdbusplus::exception_t& e) 273 { 274 lg2::error( 275 "Failed to get service path in registerSOLServiceChangeCallback: {ERROR}", 276 "ERROR", e); 277 } 278 } 279 280 void procSolConfChange(sdbusplus::message_t& msg) 281 { 282 using SolConfVariant = std::variant<bool, uint8_t>; 283 using SolConfProperties = 284 std::vector<std::pair<std::string, SolConfVariant>>; 285 286 std::string iface; 287 SolConfProperties properties; 288 289 try 290 { 291 msg.read(iface, properties); 292 } 293 catch (const std::exception& e) 294 { 295 lg2::error("procSolConfChange get properties FAIL: {ERROR}", "ERROR", 296 e); 297 return; 298 } 299 300 for (const auto& prop : properties) 301 { 302 if (prop.first == "Progress") 303 { 304 sol::Manager::get().progress = std::get<uint8_t>(prop.second); 305 } 306 else if (prop.first == "Enable") 307 { 308 sol::Manager::get().enable = std::get<bool>(prop.second); 309 } 310 else if (prop.first == "ForceEncryption") 311 { 312 sol::Manager::get().forceEncrypt = std::get<bool>(prop.second); 313 } 314 else if (prop.first == "ForceAuthentication") 315 { 316 sol::Manager::get().forceAuth = std::get<bool>(prop.second); 317 } 318 else if (prop.first == "Privilege") 319 { 320 sol::Manager::get().solMinPrivilege = 321 static_cast<session::Privilege>(std::get<uint8_t>(prop.second)); 322 } 323 else if (prop.first == "AccumulateIntervalMS") 324 { 325 sol::Manager::get().accumulateInterval = 326 std::get<uint8_t>(prop.second) * sol::accIntervalFactor * 1ms; 327 } 328 else if (prop.first == "Threshold") 329 { 330 sol::Manager::get().sendThreshold = std::get<uint8_t>(prop.second); 331 } 332 else if (prop.first == "RetryCount") 333 { 334 sol::Manager::get().retryCount = std::get<uint8_t>(prop.second); 335 } 336 else if (prop.first == "RetryIntervalMS") 337 { 338 sol::Manager::get().retryInterval = 339 std::get<uint8_t>(prop.second) * sol::retryIntervalFactor * 1ms; 340 } 341 } 342 } 343 344 void registerSolConfChangeCallbackHandler(std::string channel) 345 { 346 if (solConfPropertiesSignal == nullptr) 347 { 348 using namespace sdbusplus::bus::match::rules; 349 sdbusplus::bus_t bus{ipmid_get_sd_bus_connection()}; 350 try 351 { 352 auto servicePath = solPath + channel; 353 354 solConfPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>( 355 bus, propertiesChangedNamespace(servicePath, solInterface), 356 procSolConfChange); 357 } 358 catch (const sdbusplus::exception_t& e) 359 { 360 lg2::error( 361 "Failed to get service path in registerSolConfChangeCallbackHandler, channel: {CHANNEL}, error: {ERROR}", 362 "CHANNEL", channel, "ERROR", e); 363 } 364 } 365 return; 366 } 367 368 } // namespace sol 369