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 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 log<level::ERR>("Reading ready count from host console socket failed:", 53 entry("EXCEPTION=%s", ec.message().c_str())); 54 return; 55 } 56 std::vector<uint8_t> buffer(readSize); 57 ec.clear(); 58 size_t readDataLen = 59 consoleSocket->read_some(boost::asio::buffer(buffer), ec); 60 if (ec) 61 { 62 log<level::ERR>("Reading from host console socket failed:", 63 entry("EXCEPTION=%s", ec.message().c_str())); 64 return; 65 } 66 67 // Update the Console buffer with data read from the socket 68 buffer.resize(readDataLen); 69 dataBuffer.write(buffer); 70 } 71 72 int Manager::writeConsoleSocket(const std::vector<uint8_t>& input) const 73 { 74 boost::system::error_code ec; 75 boost::asio::write(*consoleSocket, boost::asio::buffer(input), ec); 76 return ec.value(); 77 } 78 79 void Manager::startHostConsole() 80 { 81 if (!consoleSocket) 82 { 83 initConsoleSocket(); 84 } 85 86 // Register callback to close SOL session for disable SSH SOL 87 if (matchPtrSOL == nullptr) 88 { 89 registerSOLServiceChangeCallback(); 90 } 91 92 consoleSocket->async_wait(boost::asio::socket_base::wait_read, 93 [this](const boost::system::error_code& ec) { 94 if (!ec) 95 { 96 consoleInputHandler(); 97 startHostConsole(); 98 } 99 }); 100 } // namespace sol 101 102 void Manager::stopHostConsole() 103 { 104 if (consoleSocket) 105 { 106 consoleSocket->cancel(); 107 consoleSocket.reset(); 108 } 109 } 110 111 void Manager::updateSOLParameter(uint8_t channelNum) 112 { 113 std::variant<uint8_t, bool> value; 114 sdbusplus::bus::bus dbus(ipmid_get_sd_bus_connection()); 115 static std::string solService{}; 116 ipmi::PropertyMap properties; 117 std::string ethdevice = ipmi::getChannelName(channelNum); 118 std::string solPathWitheEthName = solPath + ethdevice; 119 if (solService.empty()) 120 { 121 try 122 { 123 solService = 124 ipmi::getService(dbus, solInterface, solPathWitheEthName); 125 } 126 catch (const std::runtime_error& e) 127 { 128 solService.clear(); 129 phosphor::logging::log<phosphor::logging::level::ERR>( 130 "Error: get SOL service failed"); 131 return; 132 } 133 } 134 try 135 { 136 properties = ipmi::getAllDbusProperties( 137 dbus, solService, solPathWitheEthName, solInterface); 138 } 139 catch (const std::runtime_error&) 140 { 141 phosphor::logging::log<phosphor::logging::level::ERR>( 142 "Error setting sol parameter"); 143 return; 144 } 145 146 progress = std::get<uint8_t>(properties["Progress"]); 147 148 enable = std::get<bool>(properties["Enable"]); 149 150 forceEncrypt = std::get<bool>(properties["ForceEncryption"]); 151 152 forceAuth = std::get<bool>(properties["ForceAuthentication"]); 153 154 solMinPrivilege = static_cast<session::Privilege>( 155 std::get<uint8_t>(properties["Privilege"])); 156 157 accumulateInterval = 158 std::get<uint8_t>((properties["AccumulateIntervalMS"])) * 159 sol::accIntervalFactor * 1ms; 160 161 sendThreshold = std::get<uint8_t>(properties["Threshold"]); 162 163 retryCount = std::get<uint8_t>(properties["RetryCount"]); 164 165 retryInterval = std::get<uint8_t>(properties["RetryIntervalMS"]) * 166 sol::retryIntervalFactor * 1ms; 167 168 return; 169 } 170 171 void Manager::startPayloadInstance(uint8_t payloadInstance, 172 session::SessionID sessionID) 173 { 174 if (payloadMap.empty()) 175 { 176 try 177 { 178 startHostConsole(); 179 } 180 catch (const std::exception& e) 181 { 182 log<level::ERR>("Encountered exception when starting host console. " 183 "Hence stopping host console.", 184 entry("EXCEPTION=%s", e.what())); 185 stopHostConsole(); 186 throw; 187 } 188 } 189 190 // Create the SOL Context data for payload instance 191 std::shared_ptr<Context> context = Context::makeContext( 192 io, retryCount, sendThreshold, payloadInstance, sessionID); 193 194 payloadMap.emplace(payloadInstance, std::move(context)); 195 } 196 197 void Manager::stopPayloadInstance(uint8_t payloadInstance) 198 { 199 auto iter = payloadMap.find(payloadInstance); 200 if (iter == payloadMap.end()) 201 { 202 throw std::runtime_error("SOL Payload instance not found "); 203 } 204 205 payloadMap.erase(iter); 206 207 if (payloadMap.empty()) 208 { 209 stopHostConsole(); 210 211 dataBuffer.erase(dataBuffer.size()); 212 } 213 } 214 215 void Manager::stopAllPayloadInstance() 216 { 217 // Erase all payload instance 218 payloadMap.erase(payloadMap.begin(), payloadMap.end()); 219 220 stopHostConsole(); 221 222 dataBuffer.erase(dataBuffer.size()); 223 } 224 225 void registerSOLServiceChangeCallback() 226 { 227 using namespace sdbusplus::bus::match::rules; 228 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 229 try 230 { 231 auto servicePath = ipmi::getDbusObject( 232 bus, "xyz.openbmc_project.Control.Service.Attributes", 233 "/xyz/openbmc_project/control/service", "obmc_2dconsole"); 234 235 if (!std::empty(servicePath.first)) 236 { 237 matchPtrSOL = std::make_unique<sdbusplus::bus::match_t>( 238 bus, 239 path_namespace(servicePath.first) + 240 "arg0namespace='xyz.openbmc_project.Control.Service." 241 "Attributes'" 242 ", " + 243 type::signal() + member("PropertiesChanged") + 244 interface("org.freedesktop.DBus.Properties"), 245 [](sdbusplus::message::message& msg) { 246 std::string intfName; 247 std::map<std::string, std::variant<bool>> properties; 248 msg.read(intfName, properties); 249 250 const auto it = properties.find("Enabled"); 251 if (it != properties.end()) 252 { 253 const bool* state = std::get_if<bool>(&it->second); 254 255 if (state != nullptr && *state == false) 256 { 257 // Stop all the payload session. 258 sol::Manager::get().stopAllPayloadInstance(); 259 } 260 } 261 }); 262 } 263 } 264 catch (const sdbusplus::exception_t& e) 265 { 266 log<level::ERR>( 267 "Failed to get service path in registerSOLServiceChangeCallback"); 268 } 269 } 270 271 } // namespace sol 272