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 18 namespace sol 19 { 20 21 using namespace phosphor::logging; 22 23 std::unique_ptr<sdbusplus::bus::match_t> matchPtrSOL(nullptr); 24 25 void Manager::initConsoleSocket() 26 { 27 // explicit length constructor for NUL-prefixed abstract path 28 std::string path(CONSOLE_SOCKET_PATH, CONSOLE_SOCKET_PATH_LEN); 29 boost::asio::local::stream_protocol::endpoint ep(path); 30 consoleSocket = 31 std::make_unique<boost::asio::local::stream_protocol::socket>(*io); 32 consoleSocket->connect(ep); 33 } 34 35 void Manager::consoleInputHandler() 36 { 37 boost::system::error_code ec; 38 boost::asio::socket_base::bytes_readable cmd(true); 39 consoleSocket->io_control(cmd, ec); 40 size_t readSize; 41 if (!ec) 42 { 43 readSize = cmd.get(); 44 } 45 else 46 { 47 log<level::ERR>("Reading ready count from host console socket failed:", 48 entry("EXCEPTION=%s", ec.message().c_str())); 49 return; 50 } 51 std::vector<uint8_t> buffer(readSize); 52 ec.clear(); 53 size_t readDataLen = 54 consoleSocket->read_some(boost::asio::buffer(buffer), ec); 55 if (ec) 56 { 57 log<level::ERR>("Reading from host console socket failed:", 58 entry("EXCEPTION=%s", ec.message().c_str())); 59 return; 60 } 61 62 // Update the Console buffer with data read from the socket 63 buffer.resize(readDataLen); 64 dataBuffer.write(buffer); 65 } 66 67 int Manager::writeConsoleSocket(const std::vector<uint8_t>& input) const 68 { 69 boost::system::error_code ec; 70 boost::asio::write(*consoleSocket, boost::asio::buffer(input), ec); 71 return ec.value(); 72 } 73 74 void Manager::startHostConsole() 75 { 76 if (!consoleSocket) 77 { 78 initConsoleSocket(); 79 } 80 81 // Register callback to close SOL session for disable SSH SOL 82 if (matchPtrSOL == nullptr) 83 { 84 registerSOLServiceChangeCallback(); 85 } 86 87 consoleSocket->async_wait(boost::asio::socket_base::wait_read, 88 [this](const boost::system::error_code& ec) { 89 if (!ec) 90 { 91 consoleInputHandler(); 92 startHostConsole(); 93 } 94 }); 95 } // namespace sol 96 97 void Manager::stopHostConsole() 98 { 99 if (consoleSocket) 100 { 101 consoleSocket->cancel(); 102 consoleSocket.reset(); 103 } 104 } 105 106 void Manager::startPayloadInstance(uint8_t payloadInstance, 107 session::SessionID sessionID) 108 { 109 if (payloadMap.empty()) 110 { 111 try 112 { 113 startHostConsole(); 114 } 115 catch (const std::exception& e) 116 { 117 log<level::ERR>("Encountered exception when starting host console. " 118 "Hence stopping host console.", 119 entry("EXCEPTION=%s", e.what())); 120 stopHostConsole(); 121 throw; 122 } 123 } 124 125 // Create the SOL Context data for payload instance 126 std::shared_ptr<Context> context = Context::makeContext( 127 io, retryCount, sendThreshold, payloadInstance, sessionID); 128 129 payloadMap.emplace(payloadInstance, std::move(context)); 130 } 131 132 void Manager::stopPayloadInstance(uint8_t payloadInstance) 133 { 134 auto iter = payloadMap.find(payloadInstance); 135 if (iter == payloadMap.end()) 136 { 137 throw std::runtime_error("SOL Payload instance not found "); 138 } 139 140 payloadMap.erase(iter); 141 142 if (payloadMap.empty()) 143 { 144 stopHostConsole(); 145 146 dataBuffer.erase(dataBuffer.size()); 147 } 148 } 149 150 void Manager::stopAllPayloadInstance() 151 { 152 // Erase all payload instance 153 payloadMap.erase(payloadMap.begin(), payloadMap.end()); 154 155 stopHostConsole(); 156 157 dataBuffer.erase(dataBuffer.size()); 158 } 159 160 void registerSOLServiceChangeCallback() 161 { 162 using namespace sdbusplus::bus::match::rules; 163 sdbusplus::bus::bus bus{ipmid_get_sd_bus_connection()}; 164 try 165 { 166 auto servicePath = ipmi::getDbusObject( 167 bus, "xyz.openbmc_project.Control.Service.Attributes", 168 "/xyz/openbmc_project/control/service", "obmc_2dconsole"); 169 170 if (!std::empty(servicePath.first)) 171 { 172 matchPtrSOL = std::make_unique<sdbusplus::bus::match_t>( 173 bus, 174 path_namespace(servicePath.first) + 175 "arg0namespace='xyz.openbmc_project.Control.Service." 176 "Attributes'" 177 ", " + 178 type::signal() + member("PropertiesChanged") + 179 interface("org.freedesktop.DBus.Properties"), 180 [](sdbusplus::message::message& msg) { 181 std::string intfName; 182 std::map<std::string, std::variant<bool>> properties; 183 msg.read(intfName, properties); 184 185 const auto it = properties.find("Enabled"); 186 if (it != properties.end()) 187 { 188 const bool* state = std::get_if<bool>(&it->second); 189 190 if (state != nullptr && *state == false) 191 { 192 // Stop all the payload session. 193 std::get<sol::Manager&>(singletonPool) 194 .stopAllPayloadInstance(); 195 } 196 } 197 }); 198 } 199 } 200 catch (sdbusplus::exception_t& e) 201 { 202 log<level::ERR>( 203 "Failed to get service path in registerSOLServiceChangeCallback"); 204 } 205 } 206 207 } // namespace sol 208