blob: 215f7259895e0d544be5090bb6d2fe6c4d1b9a86 [file] [log] [blame]
#include "sol_context.hpp"
#include "main.hpp"
#include "sd_event_loop.hpp"
#include "sol_manager.hpp"
#include <phosphor-logging/log.hpp>
namespace sol
{
using namespace phosphor::logging;
Context::Context(std::shared_ptr<boost::asio::io_context> io,
uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance,
session::SessionID sessionID) :
accumulateTimer(*io),
retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount),
sendThreshold(sendThreshold), payloadInstance(instance),
sessionID(sessionID)
{
session = std::get<session::Manager&>(singletonPool).getSession(sessionID);
enableAccumulateTimer(true);
}
void Context::enableAccumulateTimer(bool enable)
{
// fetch the timeout from the SOL manager
std::chrono::microseconds interval =
std::get<sol::Manager&>(singletonPool).accumulateInterval;
if (enable)
{
accumulateTimer.expires_after(interval);
accumulateTimer.async_wait([this](const boost::system::error_code& ec) {
if (!ec)
{
charAccTimerHandler();
}
});
}
else
{
accumulateTimer.cancel();
}
}
void Context::enableRetryTimer(bool enable)
{
if (enable)
{
// fetch the timeout from the SOL manager
std::chrono::microseconds interval =
std::get<sol::Manager&>(singletonPool).retryInterval;
retryTimer.expires_after(interval);
retryTimer.async_wait([this](const boost::system::error_code& ec) {
if (!ec)
{
retryTimerHandler();
}
});
}
else
{
retryTimer.cancel();
}
}
void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
uint8_t count, bool status,
const std::vector<uint8_t>& input)
{
uint8_t respAckSeqNum = 0;
uint8_t acceptedCount = 0;
auto ack = false;
/*
* Check if the Inbound sequence number is same as the expected one.
* If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
* outstanding sequence numbers are not supported in this version of the SOL
* specification. Retried packets use the same sequence number as the first
* packet.
*/
if (seqNum && (seqNum != seqNums.get(true)))
{
log<level::INFO>("Out of sequence SOL packet - packet is dropped");
return;
}
/*
* Check if the expected ACK/NACK sequence number is same as the
* ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
* number is 0, then it is an informational packet. No request packet being
* ACK'd or NACK'd.
*/
if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
{
log<level::INFO>("Out of sequence ack number - SOL packet is dropped");
return;
}
/*
* Retry the SOL payload packet in the following conditions:
*
* a) NACK in Operation/Status
* b) Accepted Character Count does not match with the sent out SOL payload
* c) Non-zero Packet ACK/NACK Sequence Number
*/
if (status || ((count != expectedCharCount) && ackSeqNum))
{
resendPayload(noClear);
enableRetryTimer(false);
enableRetryTimer(true);
return;
}
/*
* Clear the sent data once the acknowledgment sequence number matches
* and the expected character count matches.
*/
else if ((count == expectedCharCount) && ackSeqNum)
{
// Clear the Host Console Buffer
std::get<sol::Manager&>(singletonPool).dataBuffer.erase(count);
// Once it is acknowledged stop the retry interval timer
enableRetryTimer(false);
retryCounter = maxRetryCount;
expectedCharCount = 0;
payloadCache.clear();
}
// Write character data to the Host Console
if (!input.empty() && seqNum)
{
auto rc =
std::get<sol::Manager&>(singletonPool).writeConsoleSocket(input);
if (rc)
{
log<level::ERR>("Writing to console socket descriptor failed");
ack = true;
}
else
{
respAckSeqNum = seqNum;
ack = false;
acceptedCount = input.size();
}
}
/*
* SOL payload with no character data and valid sequence number can be used
* as method to keep the SOL session active.
*/
else if (input.empty() && seqNum)
{
respAckSeqNum = seqNum;
}
if (seqNum != 0)
{
seqNums.incInboundSeqNum();
prepareResponse(respAckSeqNum, acceptedCount, ack);
}
else
{
enableAccumulateTimer(true);
}
}
void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
{
auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
/* Sent a ACK only response */
if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
{
enableAccumulateTimer(true);
std::vector<uint8_t> outPayload(sizeof(Payload));
auto response = reinterpret_cast<Payload*>(outPayload.data());
response->packetSeqNum = 0;
response->packetAckSeqNum = ackSeqNum;
response->acceptedCharCount = count;
response->outOperation.ack = ack;
sendPayload(outPayload);
return;
}
auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
payloadCache.resize(sizeof(Payload) + readSize);
auto response = reinterpret_cast<Payload*>(payloadCache.data());
response->packetAckSeqNum = ackSeqNum;
response->acceptedCharCount = count;
response->outOperation.ack = ack;
response->packetSeqNum = seqNums.incOutboundSeqNum();
auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read();
std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
expectedCharCount = readSize;
enableRetryTimer(true);
enableAccumulateTimer(false);
sendPayload(payloadCache);
}
int Context::sendOutboundPayload()
{
if (payloadCache.size() != 0)
{
enableAccumulateTimer(true);
return -1;
}
auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
payloadCache.resize(sizeof(Payload) + readSize);
auto response = reinterpret_cast<Payload*>(payloadCache.data());
response->packetAckSeqNum = 0;
response->acceptedCharCount = 0;
response->outOperation.ack = false;
response->packetSeqNum = seqNums.incOutboundSeqNum();
auto handle = std::get<sol::Manager&>(singletonPool).dataBuffer.read();
std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
expectedCharCount = readSize;
enableRetryTimer(true);
enableAccumulateTimer(false);
sendPayload(payloadCache);
return 0;
}
void Context::resendPayload(bool clear)
{
sendPayload(payloadCache);
if (clear)
{
payloadCache.clear();
expectedCharCount = 0;
std::get<sol::Manager&>(singletonPool)
.dataBuffer.erase(expectedCharCount);
}
}
void Context::sendPayload(const std::vector<uint8_t>& out) const
{
message::Handler msgHandler(session->channelPtr, sessionID);
msgHandler.sendSOLPayload(out);
}
void Context::charAccTimerHandler()
{
auto bufferSize = std::get<sol::Manager&>(singletonPool).dataBuffer.size();
try
{
if (bufferSize > 0)
{
int rc = sendOutboundPayload();
if (rc == 0)
{
return;
}
}
enableAccumulateTimer(true);
}
catch (std::exception& e)
{
log<level::ERR>(e.what());
}
}
void Context::retryTimerHandler()
{
try
{
if (retryCounter)
{
--retryCounter;
enableRetryTimer(true);
resendPayload(sol::Context::noClear);
}
else
{
retryCounter = maxRetryCount;
resendPayload(sol::Context::clear);
enableRetryTimer(false);
enableAccumulateTimer(true);
}
}
catch (std::exception& e)
{
log<level::ERR>(e.what());
}
}
} // namespace sol