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