19e801a2bSVernon Mauery #include "sol_context.hpp"
29e801a2bSVernon Mauery
3fbcac2e7STom Joseph #include "main.hpp"
42085ae07SVernon Mauery #include "message_handler.hpp"
5fbcac2e7STom Joseph #include "sd_event_loop.hpp"
62085ae07SVernon Mauery #include "sessions_manager.hpp"
7fbcac2e7STom Joseph #include "sol_manager.hpp"
8fbcac2e7STom Joseph
97b7f25f7SGeorge Liu #include <errno.h>
107b7f25f7SGeorge Liu
117b7f25f7SGeorge Liu #include <phosphor-logging/lg2.hpp>
129e801a2bSVernon Mauery
13fbcac2e7STom Joseph namespace sol
14fbcac2e7STom Joseph {
15fbcac2e7STom Joseph using namespace phosphor::logging;
16fbcac2e7STom Joseph
Context(std::shared_ptr<boost::asio::io_context> io,uint8_t maxRetryCount,uint8_t sendThreshold,uint8_t instance,session::SessionID sessionID)176f353e86SVernon Mauery Context::Context(std::shared_ptr<boost::asio::io_context> io,
186f353e86SVernon Mauery uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance,
196f353e86SVernon Mauery session::SessionID sessionID) :
206f353e86SVernon Mauery accumulateTimer(*io),
216f353e86SVernon Mauery retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount),
226f353e86SVernon Mauery sendThreshold(sendThreshold), payloadInstance(instance),
236f353e86SVernon Mauery sessionID(sessionID)
246f353e86SVernon Mauery {
252085ae07SVernon Mauery session = session::Manager::get().getSession(sessionID);
26a6ad5e16SVernon Mauery }
27a6ad5e16SVernon Mauery
28a6ad5e16SVernon Mauery std::shared_ptr<Context>
makeContext(std::shared_ptr<boost::asio::io_context> io,uint8_t maxRetryCount,uint8_t sendThreshold,uint8_t instance,session::SessionID sessionID)29a6ad5e16SVernon Mauery Context::makeContext(std::shared_ptr<boost::asio::io_context> io,
30a6ad5e16SVernon Mauery uint8_t maxRetryCount, uint8_t sendThreshold,
31a6ad5e16SVernon Mauery uint8_t instance, session::SessionID sessionID)
32a6ad5e16SVernon Mauery {
33a6ad5e16SVernon Mauery auto ctx = std::make_shared<Context>(io, maxRetryCount, sendThreshold,
34a6ad5e16SVernon Mauery instance, sessionID);
35a6ad5e16SVernon Mauery ctx->enableAccumulateTimer(true);
36a6ad5e16SVernon Mauery return ctx;
376f353e86SVernon Mauery }
386f353e86SVernon Mauery
enableAccumulateTimer(bool enable)396f353e86SVernon Mauery void Context::enableAccumulateTimer(bool enable)
406f353e86SVernon Mauery {
416f353e86SVernon Mauery // fetch the timeout from the SOL manager
422085ae07SVernon Mauery std::chrono::microseconds interval = sol::Manager::get().accumulateInterval;
4315535143STang Yiwei
446f353e86SVernon Mauery if (enable)
456f353e86SVernon Mauery {
4615535143STang Yiwei auto bufferSize = sol::Manager::get().dataBuffer.size();
4715535143STang Yiwei if (bufferSize > sendThreshold)
4815535143STang Yiwei {
49*426fcab8SKonstantin Aladyshev try
505499bf86SJian Zhang {
51*426fcab8SKonstantin Aladyshev int rc = sendOutboundPayload();
52*426fcab8SKonstantin Aladyshev if (rc == 0)
53*426fcab8SKonstantin Aladyshev {
5415535143STang Yiwei return;
5515535143STang Yiwei }
56*426fcab8SKonstantin Aladyshev }
57*426fcab8SKonstantin Aladyshev catch (const std::exception& e)
58*426fcab8SKonstantin Aladyshev {
59*426fcab8SKonstantin Aladyshev lg2::error(
60*426fcab8SKonstantin Aladyshev "Failed to call the sendOutboundPayload method: {ERROR}",
61*426fcab8SKonstantin Aladyshev "ERROR", e);
62*426fcab8SKonstantin Aladyshev return;
63*426fcab8SKonstantin Aladyshev }
64*426fcab8SKonstantin Aladyshev }
656f353e86SVernon Mauery accumulateTimer.expires_after(interval);
66*426fcab8SKonstantin Aladyshev std::weak_ptr<Context> weakRef = weak_from_this();
67a6ad5e16SVernon Mauery accumulateTimer.async_wait(
68a6ad5e16SVernon Mauery [weakRef](const boost::system::error_code& ec) {
69a6ad5e16SVernon Mauery std::shared_ptr<Context> self = weakRef.lock();
70a6ad5e16SVernon Mauery if (!ec && self)
716f353e86SVernon Mauery {
72a6ad5e16SVernon Mauery self->charAccTimerHandler();
736f353e86SVernon Mauery }
746f353e86SVernon Mauery });
756f353e86SVernon Mauery }
766f353e86SVernon Mauery else
776f353e86SVernon Mauery {
786f353e86SVernon Mauery accumulateTimer.cancel();
796f353e86SVernon Mauery }
806f353e86SVernon Mauery }
816f353e86SVernon Mauery
enableRetryTimer(bool enable)826f353e86SVernon Mauery void Context::enableRetryTimer(bool enable)
836f353e86SVernon Mauery {
846f353e86SVernon Mauery if (enable)
856f353e86SVernon Mauery {
866f353e86SVernon Mauery // fetch the timeout from the SOL manager
872085ae07SVernon Mauery std::chrono::microseconds interval = sol::Manager::get().retryInterval;
886f353e86SVernon Mauery retryTimer.expires_after(interval);
89a6ad5e16SVernon Mauery std::weak_ptr<Context> weakRef = weak_from_this();
90a6ad5e16SVernon Mauery retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
91a6ad5e16SVernon Mauery std::shared_ptr<Context> self = weakRef.lock();
92a6ad5e16SVernon Mauery if (!ec && self)
936f353e86SVernon Mauery {
94a6ad5e16SVernon Mauery self->retryTimerHandler();
956f353e86SVernon Mauery }
966f353e86SVernon Mauery });
976f353e86SVernon Mauery }
986f353e86SVernon Mauery else
996f353e86SVernon Mauery {
1006f353e86SVernon Mauery retryTimer.cancel();
1016f353e86SVernon Mauery }
1026f353e86SVernon Mauery }
1036f353e86SVernon Mauery
processInboundPayload(uint8_t seqNum,uint8_t ackSeqNum,uint8_t count,bool status,bool isBreak,const std::vector<uint8_t> & input)1049e801a2bSVernon Mauery void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
105ec437414STingting Chen uint8_t count, bool status, bool isBreak,
10670fd29cfSVernon Mauery const std::vector<uint8_t>& input)
107fbcac2e7STom Joseph {
108fbcac2e7STom Joseph uint8_t respAckSeqNum = 0;
109fbcac2e7STom Joseph uint8_t acceptedCount = 0;
110fbcac2e7STom Joseph auto ack = false;
111fbcac2e7STom Joseph
112fbcac2e7STom Joseph /*
113fbcac2e7STom Joseph * Check if the Inbound sequence number is same as the expected one.
114fbcac2e7STom Joseph * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
115fbcac2e7STom Joseph * outstanding sequence numbers are not supported in this version of the SOL
116fbcac2e7STom Joseph * specification. Retried packets use the same sequence number as the first
117fbcac2e7STom Joseph * packet.
118fbcac2e7STom Joseph */
119fbcac2e7STom Joseph if (seqNum && (seqNum != seqNums.get(true)))
120fbcac2e7STom Joseph {
1217b7f25f7SGeorge Liu lg2::info("Out of sequence SOL packet - packet is dropped");
122fbcac2e7STom Joseph return;
123fbcac2e7STom Joseph }
124fbcac2e7STom Joseph
125fbcac2e7STom Joseph /*
126fbcac2e7STom Joseph * Check if the expected ACK/NACK sequence number is same as the
127fbcac2e7STom Joseph * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
128fbcac2e7STom Joseph * number is 0, then it is an informational packet. No request packet being
129fbcac2e7STom Joseph * ACK'd or NACK'd.
130fbcac2e7STom Joseph */
131fbcac2e7STom Joseph if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
132fbcac2e7STom Joseph {
1337b7f25f7SGeorge Liu lg2::info("Out of sequence ack number - SOL packet is dropped");
134fbcac2e7STom Joseph return;
135fbcac2e7STom Joseph }
136fbcac2e7STom Joseph
137fbcac2e7STom Joseph /*
138fbcac2e7STom Joseph * Retry the SOL payload packet in the following conditions:
139fbcac2e7STom Joseph *
140fbcac2e7STom Joseph * a) NACK in Operation/Status
141fbcac2e7STom Joseph * b) Accepted Character Count does not match with the sent out SOL payload
142fbcac2e7STom Joseph * c) Non-zero Packet ACK/NACK Sequence Number
143fbcac2e7STom Joseph */
144fbcac2e7STom Joseph if (status || ((count != expectedCharCount) && ackSeqNum))
145fbcac2e7STom Joseph {
146fbcac2e7STom Joseph resendPayload(noClear);
1476f353e86SVernon Mauery enableRetryTimer(false);
1486f353e86SVernon Mauery enableRetryTimer(true);
149fbcac2e7STom Joseph return;
150fbcac2e7STom Joseph }
151fbcac2e7STom Joseph /*
152fbcac2e7STom Joseph * Clear the sent data once the acknowledgment sequence number matches
153fbcac2e7STom Joseph * and the expected character count matches.
154fbcac2e7STom Joseph */
155fbcac2e7STom Joseph else if ((count == expectedCharCount) && ackSeqNum)
156fbcac2e7STom Joseph {
157fbcac2e7STom Joseph // Clear the Host Console Buffer
1582085ae07SVernon Mauery sol::Manager::get().dataBuffer.erase(count);
159fbcac2e7STom Joseph
160fbcac2e7STom Joseph // Once it is acknowledged stop the retry interval timer
1616f353e86SVernon Mauery enableRetryTimer(false);
162fbcac2e7STom Joseph
163fbcac2e7STom Joseph retryCounter = maxRetryCount;
164fbcac2e7STom Joseph expectedCharCount = 0;
165fbcac2e7STom Joseph payloadCache.clear();
166fbcac2e7STom Joseph }
167fbcac2e7STom Joseph
168ec437414STingting Chen if (isBreak && seqNum)
169ec437414STingting Chen {
170ec437414STingting Chen lg2::info("Writing break to console socket descriptor");
171ec437414STingting Chen constexpr uint8_t sysrqValue = 72; // use this to notify sol server
172ec437414STingting Chen const std::vector<uint8_t> test{sysrqValue};
173ec437414STingting Chen auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
174ec437414STingting Chen if (ret)
175ec437414STingting Chen {
176ec437414STingting Chen lg2::error("Writing to console socket descriptor failed: {ERROR}",
177ec437414STingting Chen "ERROR", strerror(errno));
178ec437414STingting Chen }
179ec437414STingting Chen }
180ec437414STingting Chen
181ec437414STingting Chen isBreak = false;
182fbcac2e7STom Joseph // Write character data to the Host Console
183fbcac2e7STom Joseph if (!input.empty() && seqNum)
184fbcac2e7STom Joseph {
185ec437414STingting Chen auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
186fbcac2e7STom Joseph if (rc)
187fbcac2e7STom Joseph {
1887b7f25f7SGeorge Liu lg2::error("Writing to console socket descriptor failed: {ERROR}",
1897b7f25f7SGeorge Liu "ERROR", strerror(errno));
190fbcac2e7STom Joseph ack = true;
191fbcac2e7STom Joseph }
192fbcac2e7STom Joseph else
193fbcac2e7STom Joseph {
194fbcac2e7STom Joseph respAckSeqNum = seqNum;
195fbcac2e7STom Joseph ack = false;
196fbcac2e7STom Joseph acceptedCount = input.size();
197fbcac2e7STom Joseph }
198fbcac2e7STom Joseph }
199694fc0cfSTom Joseph /*
200694fc0cfSTom Joseph * SOL payload with no character data and valid sequence number can be used
201694fc0cfSTom Joseph * as method to keep the SOL session active.
202694fc0cfSTom Joseph */
203694fc0cfSTom Joseph else if (input.empty() && seqNum)
204694fc0cfSTom Joseph {
205694fc0cfSTom Joseph respAckSeqNum = seqNum;
206694fc0cfSTom Joseph }
207fbcac2e7STom Joseph
208fbcac2e7STom Joseph if (seqNum != 0)
209fbcac2e7STom Joseph {
210fbcac2e7STom Joseph seqNums.incInboundSeqNum();
211fbcac2e7STom Joseph prepareResponse(respAckSeqNum, acceptedCount, ack);
212fbcac2e7STom Joseph }
213fbcac2e7STom Joseph else
214fbcac2e7STom Joseph {
2156f353e86SVernon Mauery enableAccumulateTimer(true);
216fbcac2e7STom Joseph }
217fbcac2e7STom Joseph }
218fbcac2e7STom Joseph
prepareResponse(uint8_t ackSeqNum,uint8_t count,bool ack)21975e15db4STom Joseph void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
22075e15db4STom Joseph {
2212085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size();
22275e15db4STom Joseph
22375e15db4STom Joseph /* Sent a ACK only response */
22475e15db4STom Joseph if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
22575e15db4STom Joseph {
2266f353e86SVernon Mauery enableAccumulateTimer(true);
22775e15db4STom Joseph
22870fd29cfSVernon Mauery std::vector<uint8_t> outPayload(sizeof(Payload));
22975e15db4STom Joseph auto response = reinterpret_cast<Payload*>(outPayload.data());
23075e15db4STom Joseph response->packetSeqNum = 0;
23175e15db4STom Joseph response->packetAckSeqNum = ackSeqNum;
23275e15db4STom Joseph response->acceptedCharCount = count;
23375e15db4STom Joseph response->outOperation.ack = ack;
23475e15db4STom Joseph sendPayload(outPayload);
23575e15db4STom Joseph return;
23675e15db4STom Joseph }
23775e15db4STom Joseph
23875e15db4STom Joseph auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
23975e15db4STom Joseph payloadCache.resize(sizeof(Payload) + readSize);
24075e15db4STom Joseph auto response = reinterpret_cast<Payload*>(payloadCache.data());
24175e15db4STom Joseph response->packetAckSeqNum = ackSeqNum;
24275e15db4STom Joseph response->acceptedCharCount = count;
24375e15db4STom Joseph response->outOperation.ack = ack;
24475e15db4STom Joseph response->packetSeqNum = seqNums.incOutboundSeqNum();
24575e15db4STom Joseph
2462085ae07SVernon Mauery auto handle = sol::Manager::get().dataBuffer.read();
24775e15db4STom Joseph std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
24875e15db4STom Joseph expectedCharCount = readSize;
24975e15db4STom Joseph
2506f353e86SVernon Mauery enableRetryTimer(true);
2516f353e86SVernon Mauery enableAccumulateTimer(false);
25275e15db4STom Joseph
25375e15db4STom Joseph sendPayload(payloadCache);
25475e15db4STom Joseph }
25575e15db4STom Joseph
sendOutboundPayload()2567306314fSTom Joseph int Context::sendOutboundPayload()
2577306314fSTom Joseph {
2587306314fSTom Joseph if (payloadCache.size() != 0)
2597306314fSTom Joseph {
2607306314fSTom Joseph return -1;
2617306314fSTom Joseph }
2627306314fSTom Joseph
2632085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size();
2647306314fSTom Joseph auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
2657306314fSTom Joseph
2667306314fSTom Joseph payloadCache.resize(sizeof(Payload) + readSize);
2677306314fSTom Joseph auto response = reinterpret_cast<Payload*>(payloadCache.data());
2687306314fSTom Joseph response->packetAckSeqNum = 0;
2697306314fSTom Joseph response->acceptedCharCount = 0;
2707306314fSTom Joseph response->outOperation.ack = false;
2717306314fSTom Joseph response->packetSeqNum = seqNums.incOutboundSeqNum();
2727306314fSTom Joseph
2732085ae07SVernon Mauery auto handle = sol::Manager::get().dataBuffer.read();
2747306314fSTom Joseph std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
2757306314fSTom Joseph expectedCharCount = readSize;
2767306314fSTom Joseph
2776f353e86SVernon Mauery enableRetryTimer(true);
2786f353e86SVernon Mauery enableAccumulateTimer(false);
2797306314fSTom Joseph
2807306314fSTom Joseph sendPayload(payloadCache);
2817306314fSTom Joseph
2827306314fSTom Joseph return 0;
2837306314fSTom Joseph }
2847306314fSTom Joseph
resendPayload(bool clear)28575e15db4STom Joseph void Context::resendPayload(bool clear)
286fbcac2e7STom Joseph {
2872fd466f4STom Joseph sendPayload(payloadCache);
288fbcac2e7STom Joseph
2892fd466f4STom Joseph if (clear)
2902fd466f4STom Joseph {
2912fd466f4STom Joseph payloadCache.clear();
2922fd466f4STom Joseph expectedCharCount = 0;
2932085ae07SVernon Mauery sol::Manager::get().dataBuffer.erase(expectedCharCount);
2942fd466f4STom Joseph }
295fbcac2e7STom Joseph }
296fbcac2e7STom Joseph
sendPayload(const std::vector<uint8_t> & out) const29770fd29cfSVernon Mauery void Context::sendPayload(const std::vector<uint8_t>& out) const
298fbcac2e7STom Joseph {
29922596f21STom Joseph message::Handler msgHandler(session->channelPtr, sessionID);
30022596f21STom Joseph
30122596f21STom Joseph msgHandler.sendSOLPayload(out);
302fbcac2e7STom Joseph }
303fbcac2e7STom Joseph
charAccTimerHandler()3046f353e86SVernon Mauery void Context::charAccTimerHandler()
3056f353e86SVernon Mauery {
3062085ae07SVernon Mauery auto bufferSize = sol::Manager::get().dataBuffer.size();
3076f353e86SVernon Mauery
3086f353e86SVernon Mauery try
3096f353e86SVernon Mauery {
3106f353e86SVernon Mauery if (bufferSize > 0)
3116f353e86SVernon Mauery {
3126f353e86SVernon Mauery int rc = sendOutboundPayload();
3136f353e86SVernon Mauery if (rc == 0)
3146f353e86SVernon Mauery {
3156f353e86SVernon Mauery return;
3166f353e86SVernon Mauery }
3176f353e86SVernon Mauery }
3186f353e86SVernon Mauery enableAccumulateTimer(true);
3196f353e86SVernon Mauery }
32012d199b2SPatrick Williams catch (const std::exception& e)
3216f353e86SVernon Mauery {
3227b7f25f7SGeorge Liu lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
3237b7f25f7SGeorge Liu "ERROR", e);
3246f353e86SVernon Mauery }
3256f353e86SVernon Mauery }
3266f353e86SVernon Mauery
retryTimerHandler()3276f353e86SVernon Mauery void Context::retryTimerHandler()
3286f353e86SVernon Mauery {
3296f353e86SVernon Mauery try
3306f353e86SVernon Mauery {
3316f353e86SVernon Mauery if (retryCounter)
3326f353e86SVernon Mauery {
3336f353e86SVernon Mauery --retryCounter;
3346f353e86SVernon Mauery enableRetryTimer(true);
3356f353e86SVernon Mauery resendPayload(sol::Context::noClear);
3366f353e86SVernon Mauery }
3376f353e86SVernon Mauery else
3386f353e86SVernon Mauery {
3396f353e86SVernon Mauery retryCounter = maxRetryCount;
3406f353e86SVernon Mauery resendPayload(sol::Context::clear);
3416f353e86SVernon Mauery enableRetryTimer(false);
3426f353e86SVernon Mauery enableAccumulateTimer(true);
3436f353e86SVernon Mauery }
3446f353e86SVernon Mauery }
34512d199b2SPatrick Williams catch (const std::exception& e)
3466f353e86SVernon Mauery {
3477b7f25f7SGeorge Liu lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
3486f353e86SVernon Mauery }
3496f353e86SVernon Mauery }
350fbcac2e7STom Joseph } // namespace sol
351