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