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