1 #include "sol_context.hpp" 2 3 #include "main.hpp" 4 #include "message_handler.hpp" 5 #include "sd_event_loop.hpp" 6 #include "sessions_manager.hpp" 7 #include "sol_manager.hpp" 8 9 #include <errno.h> 10 11 #include <phosphor-logging/lg2.hpp> 12 13 namespace sol 14 { 15 using namespace phosphor::logging; 16 17 Context::Context(std::shared_ptr<boost::asio::io_context> io, 18 uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance, 19 session::SessionID sessionID) : 20 accumulateTimer(*io), 21 retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount), 22 sendThreshold(sendThreshold), payloadInstance(instance), 23 sessionID(sessionID) 24 { 25 session = session::Manager::get().getSession(sessionID); 26 } 27 28 std::shared_ptr<Context> 29 Context::makeContext(std::shared_ptr<boost::asio::io_context> io, 30 uint8_t maxRetryCount, uint8_t sendThreshold, 31 uint8_t instance, session::SessionID sessionID) 32 { 33 auto ctx = std::make_shared<Context>(io, maxRetryCount, sendThreshold, 34 instance, sessionID); 35 ctx->enableAccumulateTimer(true); 36 return ctx; 37 } 38 39 void Context::enableAccumulateTimer(bool enable) 40 { 41 // fetch the timeout from the SOL manager 42 std::chrono::microseconds interval = sol::Manager::get().accumulateInterval; 43 44 if (enable) 45 { 46 auto bufferSize = sol::Manager::get().dataBuffer.size(); 47 std::weak_ptr<Context> weakRef = weak_from_this(); 48 if (bufferSize > sendThreshold) 49 { 50 getIo()->post([weakRef]() { 51 std::shared_ptr<Context> self = weakRef.lock(); 52 if (self) 53 { 54 self->charAccTimerHandler(); 55 } 56 }); 57 return; 58 } 59 accumulateTimer.expires_after(interval); 60 accumulateTimer.async_wait( 61 [weakRef](const boost::system::error_code& ec) { 62 std::shared_ptr<Context> self = weakRef.lock(); 63 if (!ec && self) 64 { 65 self->charAccTimerHandler(); 66 } 67 }); 68 } 69 else 70 { 71 accumulateTimer.cancel(); 72 } 73 } 74 75 void Context::enableRetryTimer(bool enable) 76 { 77 if (enable) 78 { 79 // fetch the timeout from the SOL manager 80 std::chrono::microseconds interval = sol::Manager::get().retryInterval; 81 retryTimer.expires_after(interval); 82 std::weak_ptr<Context> weakRef = weak_from_this(); 83 retryTimer.async_wait([weakRef](const boost::system::error_code& ec) { 84 std::shared_ptr<Context> self = weakRef.lock(); 85 if (!ec && self) 86 { 87 self->retryTimerHandler(); 88 } 89 }); 90 } 91 else 92 { 93 retryTimer.cancel(); 94 } 95 } 96 97 void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum, 98 uint8_t count, bool status, bool isBreak, 99 const std::vector<uint8_t>& input) 100 { 101 uint8_t respAckSeqNum = 0; 102 uint8_t acceptedCount = 0; 103 auto ack = false; 104 105 /* 106 * Check if the Inbound sequence number is same as the expected one. 107 * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple 108 * outstanding sequence numbers are not supported in this version of the SOL 109 * specification. Retried packets use the same sequence number as the first 110 * packet. 111 */ 112 if (seqNum && (seqNum != seqNums.get(true))) 113 { 114 lg2::info("Out of sequence SOL packet - packet is dropped"); 115 return; 116 } 117 118 /* 119 * Check if the expected ACK/NACK sequence number is same as the 120 * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence 121 * number is 0, then it is an informational packet. No request packet being 122 * ACK'd or NACK'd. 123 */ 124 if (ackSeqNum && (ackSeqNum != seqNums.get(false))) 125 { 126 lg2::info("Out of sequence ack number - SOL packet is dropped"); 127 return; 128 } 129 130 /* 131 * Retry the SOL payload packet in the following conditions: 132 * 133 * a) NACK in Operation/Status 134 * b) Accepted Character Count does not match with the sent out SOL payload 135 * c) Non-zero Packet ACK/NACK Sequence Number 136 */ 137 if (status || ((count != expectedCharCount) && ackSeqNum)) 138 { 139 resendPayload(noClear); 140 enableRetryTimer(false); 141 enableRetryTimer(true); 142 return; 143 } 144 /* 145 * Clear the sent data once the acknowledgment sequence number matches 146 * and the expected character count matches. 147 */ 148 else if ((count == expectedCharCount) && ackSeqNum) 149 { 150 // Clear the Host Console Buffer 151 sol::Manager::get().dataBuffer.erase(count); 152 153 // Once it is acknowledged stop the retry interval timer 154 enableRetryTimer(false); 155 156 retryCounter = maxRetryCount; 157 expectedCharCount = 0; 158 payloadCache.clear(); 159 } 160 161 if (isBreak && seqNum) 162 { 163 lg2::info("Writing break to console socket descriptor"); 164 constexpr uint8_t sysrqValue = 72; // use this to notify sol server 165 const std::vector<uint8_t> test{sysrqValue}; 166 auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak); 167 if (ret) 168 { 169 lg2::error("Writing to console socket descriptor failed: {ERROR}", 170 "ERROR", strerror(errno)); 171 } 172 } 173 174 isBreak = false; 175 // Write character data to the Host Console 176 if (!input.empty() && seqNum) 177 { 178 auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak); 179 if (rc) 180 { 181 lg2::error("Writing to console socket descriptor failed: {ERROR}", 182 "ERROR", strerror(errno)); 183 ack = true; 184 } 185 else 186 { 187 respAckSeqNum = seqNum; 188 ack = false; 189 acceptedCount = input.size(); 190 } 191 } 192 /* 193 * SOL payload with no character data and valid sequence number can be used 194 * as method to keep the SOL session active. 195 */ 196 else if (input.empty() && seqNum) 197 { 198 respAckSeqNum = seqNum; 199 } 200 201 if (seqNum != 0) 202 { 203 seqNums.incInboundSeqNum(); 204 prepareResponse(respAckSeqNum, acceptedCount, ack); 205 } 206 else 207 { 208 enableAccumulateTimer(true); 209 } 210 } 211 212 void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack) 213 { 214 auto bufferSize = sol::Manager::get().dataBuffer.size(); 215 216 /* Sent a ACK only response */ 217 if (payloadCache.size() != 0 || (bufferSize < sendThreshold)) 218 { 219 enableAccumulateTimer(true); 220 221 std::vector<uint8_t> outPayload(sizeof(Payload)); 222 auto response = reinterpret_cast<Payload*>(outPayload.data()); 223 response->packetSeqNum = 0; 224 response->packetAckSeqNum = ackSeqNum; 225 response->acceptedCharCount = count; 226 response->outOperation.ack = ack; 227 sendPayload(outPayload); 228 return; 229 } 230 231 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE); 232 payloadCache.resize(sizeof(Payload) + readSize); 233 auto response = reinterpret_cast<Payload*>(payloadCache.data()); 234 response->packetAckSeqNum = ackSeqNum; 235 response->acceptedCharCount = count; 236 response->outOperation.ack = ack; 237 response->packetSeqNum = seqNums.incOutboundSeqNum(); 238 239 auto handle = sol::Manager::get().dataBuffer.read(); 240 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload)); 241 expectedCharCount = readSize; 242 243 enableRetryTimer(true); 244 enableAccumulateTimer(false); 245 246 sendPayload(payloadCache); 247 } 248 249 int Context::sendOutboundPayload() 250 { 251 if (payloadCache.size() != 0) 252 { 253 enableAccumulateTimer(true); 254 return -1; 255 } 256 257 auto bufferSize = sol::Manager::get().dataBuffer.size(); 258 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE); 259 260 payloadCache.resize(sizeof(Payload) + readSize); 261 auto response = reinterpret_cast<Payload*>(payloadCache.data()); 262 response->packetAckSeqNum = 0; 263 response->acceptedCharCount = 0; 264 response->outOperation.ack = false; 265 response->packetSeqNum = seqNums.incOutboundSeqNum(); 266 267 auto handle = sol::Manager::get().dataBuffer.read(); 268 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload)); 269 expectedCharCount = readSize; 270 271 enableRetryTimer(true); 272 enableAccumulateTimer(false); 273 274 sendPayload(payloadCache); 275 276 return 0; 277 } 278 279 void Context::resendPayload(bool clear) 280 { 281 sendPayload(payloadCache); 282 283 if (clear) 284 { 285 payloadCache.clear(); 286 expectedCharCount = 0; 287 sol::Manager::get().dataBuffer.erase(expectedCharCount); 288 } 289 } 290 291 void Context::sendPayload(const std::vector<uint8_t>& out) const 292 { 293 message::Handler msgHandler(session->channelPtr, sessionID); 294 295 msgHandler.sendSOLPayload(out); 296 } 297 298 void Context::charAccTimerHandler() 299 { 300 auto bufferSize = sol::Manager::get().dataBuffer.size(); 301 302 try 303 { 304 if (bufferSize > 0) 305 { 306 int rc = sendOutboundPayload(); 307 if (rc == 0) 308 { 309 return; 310 } 311 } 312 enableAccumulateTimer(true); 313 } 314 catch (const std::exception& e) 315 { 316 lg2::error("Failed to call the sendOutboundPayload method: {ERROR}", 317 "ERROR", e); 318 } 319 } 320 321 void Context::retryTimerHandler() 322 { 323 try 324 { 325 if (retryCounter) 326 { 327 --retryCounter; 328 enableRetryTimer(true); 329 resendPayload(sol::Context::noClear); 330 } 331 else 332 { 333 retryCounter = maxRetryCount; 334 resendPayload(sol::Context::clear); 335 enableRetryTimer(false); 336 enableAccumulateTimer(true); 337 } 338 } 339 catch (const std::exception& e) 340 { 341 lg2::error("Failed to retry timer: {ERROR}", "ERROR", e); 342 } 343 } 344 } // namespace sol 345