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 <chrono> 14 #include <cmath> 15 #include <ipmid/utils.hpp> 16 #include <phosphor-logging/log.hpp> 17 #include <sdbusplus/message/types.hpp> 18 19 constexpr const char* solInterface = "xyz.openbmc_project.Ipmi.SOL"; 20 constexpr const char* solPath = "/xyz/openbmc_project/ipmi/sol/"; 21 constexpr const char* PROP_INTF = "org.freedesktop.DBus.Properties"; 22 23 namespace sol 24 { 25 26 using namespace phosphor::logging; 27 28 std::unique_ptr<sdbusplus::bus::match_t> matchPtrSOL(nullptr); 29 std::unique_ptr<sdbusplus::bus::match_t> solConfPropertiesSignal(nullptr); 30 31 void Manager::initConsoleSocket() 32 { 33 // explicit length constructor for NUL-prefixed abstract path 34 std::string path(CONSOLE_SOCKET_PATH, CONSOLE_SOCKET_PATH_LEN); 35 boost::asio::local::stream_protocol::endpoint ep(path); 36 consoleSocket = 37 std::make_unique<boost::asio::local::stream_protocol::socket>(*io); 38 consoleSocket->connect(ep); 39 } 40 41 void Manager::consoleInputHandler() 42 { 43 boost::system::error_code ec; 44 boost::asio::socket_base::bytes_readable cmd(true); 45 consoleSocket->io_control(cmd, ec); 46 size_t readSize; 47 if (!ec) 48 { 49 readSize = cmd.get(); 50 } 51 else 52 { 53 log<level::ERR>("Reading ready count from host console socket failed:", 54 entry("EXCEPTION=%s", ec.message().c_str())); 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 log<level::ERR>("Reading from host console socket failed:", 64 entry("EXCEPTION=%s", ec.message().c_str())); 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::bus 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 phosphor::logging::log<phosphor::logging::level::ERR>( 131 "Error: get SOL service failed"); 132 return; 133 } 134 } 135 try 136 { 137 properties = ipmi::getAllDbusProperties( 138 dbus, solService, solPathWitheEthName, solInterface); 139 } 140 catch (const std::runtime_error&) 141 { 142 phosphor::logging::log<phosphor::logging::level::ERR>( 143 "Error setting sol parameter"); 144 return; 145 } 146 147 progress = std::get<uint8_t>(properties["Progress"]); 148 149 enable = std::get<bool>(properties["Enable"]); 150 151 forceEncrypt = std::get<bool>(properties["ForceEncryption"]); 152 153 forceAuth = std::get<bool>(properties["ForceAuthentication"]); 154 155 solMinPrivilege = static_cast<session::Privilege>( 156 std::get<uint8_t>(properties["Privilege"])); 157 158 accumulateInterval = 159 std::get<uint8_t>((properties["AccumulateIntervalMS"])) * 160 sol::accIntervalFactor * 1ms; 161 162 sendThreshold = std::get<uint8_t>(properties["Threshold"]); 163 164 retryCount = std::get<uint8_t>(properties["RetryCount"]); 165 166 retryInterval = std::get<uint8_t>(properties["RetryIntervalMS"]) * 167 sol::retryIntervalFactor * 1ms; 168 169 return; 170 } 171 172 void Manager::startPayloadInstance(uint8_t payloadInstance, 173 session::SessionID sessionID) 174 { 175 if (payloadMap.empty()) 176 { 177 try 178 { 179 startHostConsole(); 180 } 181 catch (const std::exception& e) 182 { 183 log<level::ERR>("Encountered exception when starting host console. " 184 "Hence stopping host console.", 185 entry("EXCEPTION=%s", e.what())); 186 stopHostConsole(); 187 throw; 188 } 189 } 190 191 // Create the SOL Context data for payload instance 192 std::shared_ptr<Context> context = Context::makeContext( 193 io, retryCount, sendThreshold, payloadInstance, sessionID); 194 195 payloadMap.emplace(payloadInstance, std::move(context)); 196 } 197 198 void Manager::stopPayloadInstance(uint8_t payloadInstance) 199 { 200 auto iter = payloadMap.find(payloadInstance); 201 if (iter == payloadMap.end()) 202 { 203 throw std::runtime_error("SOL Payload instance not found "); 204 } 205 206 payloadMap.erase(iter); 207 208 if (payloadMap.empty()) 209 { 210 stopHostConsole(); 211 212 dataBuffer.erase(dataBuffer.size()); 213 } 214 } 215 216 void Manager::stopAllPayloadInstance() 217 { 218 // Erase all payload instance 219 payloadMap.erase(payloadMap.begin(), payloadMap.end()); 220 221 stopHostConsole(); 222 223 dataBuffer.erase(dataBuffer.size()); 224 } 225 226 void registerSOLServiceChangeCallback() 227 { 228 using namespace sdbusplus::bus::match::rules; 229 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 230 try 231 { 232 auto servicePath = ipmi::getDbusObject( 233 bus, "xyz.openbmc_project.Control.Service.Attributes", 234 "/xyz/openbmc_project/control/service", "obmc_2dconsole"); 235 236 if (!std::empty(servicePath.first)) 237 { 238 matchPtrSOL = std::make_unique<sdbusplus::bus::match_t>( 239 bus, 240 path_namespace(servicePath.first) + 241 "arg0namespace='xyz.openbmc_project.Control.Service." 242 "Attributes'" 243 ", " + 244 type::signal() + member("PropertiesChanged") + 245 interface("org.freedesktop.DBus.Properties"), 246 [](sdbusplus::message::message& msg) { 247 std::string intfName; 248 std::map<std::string, std::variant<bool>> properties; 249 msg.read(intfName, properties); 250 251 const auto it = properties.find("Enabled"); 252 if (it != properties.end()) 253 { 254 const bool* state = std::get_if<bool>(&it->second); 255 256 if (state != nullptr && *state == false) 257 { 258 // Stop all the payload session. 259 sol::Manager::get().stopAllPayloadInstance(); 260 } 261 } 262 }); 263 } 264 } 265 catch (const sdbusplus::exception_t& e) 266 { 267 log<level::ERR>( 268 "Failed to get service path in registerSOLServiceChangeCallback"); 269 } 270 } 271 272 void procSolConfChange(sdbusplus::message::message& msg) 273 { 274 using SolConfVariant = std::variant<bool, uint8_t>; 275 using SolConfProperties = 276 std::vector<std::pair<std::string, SolConfVariant>>; 277 278 std::string iface; 279 SolConfProperties properties; 280 281 try 282 { 283 msg.read(iface, properties); 284 } 285 catch (const std::exception& e) 286 { 287 phosphor::logging::log<phosphor::logging::level::ERR>( 288 "procSolConfChange get properties FAIL", 289 entry("ERROR=%s", e.what())); 290 return; 291 } 292 293 for (const auto& prop : properties) 294 { 295 if (prop.first == "Progress") 296 { 297 sol::Manager::get().progress = std::get<uint8_t>(prop.second); 298 } 299 else if (prop.first == "Enable") 300 { 301 sol::Manager::get().enable = std::get<bool>(prop.second); 302 } 303 else if (prop.first == "ForceEncryption") 304 { 305 sol::Manager::get().forceEncrypt = std::get<bool>(prop.second); 306 } 307 else if (prop.first == "ForceAuthentication") 308 { 309 sol::Manager::get().forceAuth = std::get<bool>(prop.second); 310 } 311 else if (prop.first == "Privilege") 312 { 313 sol::Manager::get().solMinPrivilege = 314 static_cast<session::Privilege>(std::get<uint8_t>(prop.second)); 315 } 316 else if (prop.first == "AccumulateIntervalMS") 317 { 318 sol::Manager::get().accumulateInterval = 319 std::get<uint8_t>(prop.second) * sol::accIntervalFactor * 1ms; 320 } 321 else if (prop.first == "Threshold") 322 { 323 sol::Manager::get().sendThreshold = std::get<uint8_t>(prop.second); 324 } 325 else if (prop.first == "RetryCount") 326 { 327 sol::Manager::get().retryCount = std::get<uint8_t>(prop.second); 328 } 329 else if (prop.first == "RetryIntervalMS") 330 { 331 sol::Manager::get().retryInterval = 332 std::get<uint8_t>(prop.second) * sol::retryIntervalFactor * 1ms; 333 } 334 } 335 } 336 337 void registerSolConfChangeCallbackHandler(std::string channel) 338 { 339 if (solConfPropertiesSignal == nullptr) 340 { 341 using namespace sdbusplus::bus::match::rules; 342 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 343 try 344 { 345 auto servicePath = solPath + channel; 346 347 solConfPropertiesSignal = std::make_unique<sdbusplus::bus::match_t>( 348 bus, propertiesChangedNamespace(servicePath, solInterface), 349 procSolConfChange); 350 } 351 catch (const sdbusplus::exception_t& e) 352 { 353 log<level::ERR>("Failed to get service path in " 354 "registerSolConfChangeCallbackHandler", 355 entry("CHANNEL=%s", channel.c_str())); 356 } 357 } 358 return; 359 } 360 361 } // namespace sol 362