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 
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>
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 
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;
436f353e86SVernon Mauery     if (enable)
446f353e86SVernon Mauery     {
456f353e86SVernon Mauery         accumulateTimer.expires_after(interval);
46a6ad5e16SVernon Mauery         std::weak_ptr<Context> weakRef = weak_from_this();
47a6ad5e16SVernon Mauery         accumulateTimer.async_wait(
48a6ad5e16SVernon Mauery             [weakRef](const boost::system::error_code& ec) {
49a6ad5e16SVernon Mauery                 std::shared_ptr<Context> self = weakRef.lock();
50a6ad5e16SVernon Mauery                 if (!ec && self)
516f353e86SVernon Mauery                 {
52a6ad5e16SVernon Mauery                     self->charAccTimerHandler();
536f353e86SVernon Mauery                 }
546f353e86SVernon Mauery             });
556f353e86SVernon Mauery     }
566f353e86SVernon Mauery     else
576f353e86SVernon Mauery     {
586f353e86SVernon Mauery         accumulateTimer.cancel();
596f353e86SVernon Mauery     }
606f353e86SVernon Mauery }
616f353e86SVernon Mauery 
626f353e86SVernon Mauery void Context::enableRetryTimer(bool enable)
636f353e86SVernon Mauery {
646f353e86SVernon Mauery     if (enable)
656f353e86SVernon Mauery     {
666f353e86SVernon Mauery         // fetch the timeout from the SOL manager
672085ae07SVernon Mauery         std::chrono::microseconds interval = sol::Manager::get().retryInterval;
686f353e86SVernon Mauery         retryTimer.expires_after(interval);
69a6ad5e16SVernon Mauery         std::weak_ptr<Context> weakRef = weak_from_this();
70a6ad5e16SVernon Mauery         retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
71a6ad5e16SVernon Mauery             std::shared_ptr<Context> self = weakRef.lock();
72a6ad5e16SVernon Mauery             if (!ec && self)
736f353e86SVernon Mauery             {
74a6ad5e16SVernon Mauery                 self->retryTimerHandler();
756f353e86SVernon Mauery             }
766f353e86SVernon Mauery         });
776f353e86SVernon Mauery     }
786f353e86SVernon Mauery     else
796f353e86SVernon Mauery     {
806f353e86SVernon Mauery         retryTimer.cancel();
816f353e86SVernon Mauery     }
826f353e86SVernon Mauery }
836f353e86SVernon Mauery 
849e801a2bSVernon Mauery void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
85*ec437414STingting Chen                                     uint8_t count, bool status, bool isBreak,
8670fd29cfSVernon Mauery                                     const std::vector<uint8_t>& input)
87fbcac2e7STom Joseph {
88fbcac2e7STom Joseph     uint8_t respAckSeqNum = 0;
89fbcac2e7STom Joseph     uint8_t acceptedCount = 0;
90fbcac2e7STom Joseph     auto ack = false;
91fbcac2e7STom Joseph 
92fbcac2e7STom Joseph     /*
93fbcac2e7STom Joseph      * Check if the Inbound sequence number is same as the expected one.
94fbcac2e7STom Joseph      * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
95fbcac2e7STom Joseph      * outstanding sequence numbers are not supported in this version of the SOL
96fbcac2e7STom Joseph      * specification. Retried packets use the same sequence number as the first
97fbcac2e7STom Joseph      * packet.
98fbcac2e7STom Joseph      */
99fbcac2e7STom Joseph     if (seqNum && (seqNum != seqNums.get(true)))
100fbcac2e7STom Joseph     {
1017b7f25f7SGeorge Liu         lg2::info("Out of sequence SOL packet - packet is dropped");
102fbcac2e7STom Joseph         return;
103fbcac2e7STom Joseph     }
104fbcac2e7STom Joseph 
105fbcac2e7STom Joseph     /*
106fbcac2e7STom Joseph      * Check if the expected ACK/NACK sequence number is same as the
107fbcac2e7STom Joseph      * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
108fbcac2e7STom Joseph      * number is 0, then it is an informational packet. No request packet being
109fbcac2e7STom Joseph      * ACK'd or NACK'd.
110fbcac2e7STom Joseph      */
111fbcac2e7STom Joseph     if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
112fbcac2e7STom Joseph     {
1137b7f25f7SGeorge Liu         lg2::info("Out of sequence ack number - SOL packet is dropped");
114fbcac2e7STom Joseph         return;
115fbcac2e7STom Joseph     }
116fbcac2e7STom Joseph 
117fbcac2e7STom Joseph     /*
118fbcac2e7STom Joseph      * Retry the SOL payload packet in the following conditions:
119fbcac2e7STom Joseph      *
120fbcac2e7STom Joseph      * a) NACK in Operation/Status
121fbcac2e7STom Joseph      * b) Accepted Character Count does not match with the sent out SOL payload
122fbcac2e7STom Joseph      * c) Non-zero Packet ACK/NACK Sequence Number
123fbcac2e7STom Joseph      */
124fbcac2e7STom Joseph     if (status || ((count != expectedCharCount) && ackSeqNum))
125fbcac2e7STom Joseph     {
126fbcac2e7STom Joseph         resendPayload(noClear);
1276f353e86SVernon Mauery         enableRetryTimer(false);
1286f353e86SVernon Mauery         enableRetryTimer(true);
129fbcac2e7STom Joseph         return;
130fbcac2e7STom Joseph     }
131fbcac2e7STom Joseph     /*
132fbcac2e7STom Joseph      * Clear the sent data once the acknowledgment sequence number matches
133fbcac2e7STom Joseph      * and the expected character count matches.
134fbcac2e7STom Joseph      */
135fbcac2e7STom Joseph     else if ((count == expectedCharCount) && ackSeqNum)
136fbcac2e7STom Joseph     {
137fbcac2e7STom Joseph         // Clear the Host Console Buffer
1382085ae07SVernon Mauery         sol::Manager::get().dataBuffer.erase(count);
139fbcac2e7STom Joseph 
140fbcac2e7STom Joseph         // Once it is acknowledged stop the retry interval timer
1416f353e86SVernon Mauery         enableRetryTimer(false);
142fbcac2e7STom Joseph 
143fbcac2e7STom Joseph         retryCounter = maxRetryCount;
144fbcac2e7STom Joseph         expectedCharCount = 0;
145fbcac2e7STom Joseph         payloadCache.clear();
146fbcac2e7STom Joseph     }
147fbcac2e7STom Joseph 
148*ec437414STingting Chen     if (isBreak && seqNum)
149*ec437414STingting Chen     {
150*ec437414STingting Chen         lg2::info("Writing break to console socket descriptor");
151*ec437414STingting Chen         constexpr uint8_t sysrqValue = 72; // use this to notify sol server
152*ec437414STingting Chen         const std::vector<uint8_t> test{sysrqValue};
153*ec437414STingting Chen         auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
154*ec437414STingting Chen         if (ret)
155*ec437414STingting Chen         {
156*ec437414STingting Chen             lg2::error("Writing to console socket descriptor failed: {ERROR}",
157*ec437414STingting Chen                        "ERROR", strerror(errno));
158*ec437414STingting Chen         }
159*ec437414STingting Chen     }
160*ec437414STingting Chen 
161*ec437414STingting Chen     isBreak = false;
162fbcac2e7STom Joseph     // Write character data to the Host Console
163fbcac2e7STom Joseph     if (!input.empty() && seqNum)
164fbcac2e7STom Joseph     {
165*ec437414STingting Chen         auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
166fbcac2e7STom Joseph         if (rc)
167fbcac2e7STom Joseph         {
1687b7f25f7SGeorge Liu             lg2::error("Writing to console socket descriptor failed: {ERROR}",
1697b7f25f7SGeorge Liu                        "ERROR", strerror(errno));
170fbcac2e7STom Joseph             ack = true;
171fbcac2e7STom Joseph         }
172fbcac2e7STom Joseph         else
173fbcac2e7STom Joseph         {
174fbcac2e7STom Joseph             respAckSeqNum = seqNum;
175fbcac2e7STom Joseph             ack = false;
176fbcac2e7STom Joseph             acceptedCount = input.size();
177fbcac2e7STom Joseph         }
178fbcac2e7STom Joseph     }
179694fc0cfSTom Joseph     /*
180694fc0cfSTom Joseph      * SOL payload with no character data and valid sequence number can be used
181694fc0cfSTom Joseph      * as method to keep the SOL session active.
182694fc0cfSTom Joseph      */
183694fc0cfSTom Joseph     else if (input.empty() && seqNum)
184694fc0cfSTom Joseph     {
185694fc0cfSTom Joseph         respAckSeqNum = seqNum;
186694fc0cfSTom Joseph     }
187fbcac2e7STom Joseph 
188fbcac2e7STom Joseph     if (seqNum != 0)
189fbcac2e7STom Joseph     {
190fbcac2e7STom Joseph         seqNums.incInboundSeqNum();
191fbcac2e7STom Joseph         prepareResponse(respAckSeqNum, acceptedCount, ack);
192fbcac2e7STom Joseph     }
193fbcac2e7STom Joseph     else
194fbcac2e7STom Joseph     {
1956f353e86SVernon Mauery         enableAccumulateTimer(true);
196fbcac2e7STom Joseph     }
197fbcac2e7STom Joseph }
198fbcac2e7STom Joseph 
19975e15db4STom Joseph void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
20075e15db4STom Joseph {
2012085ae07SVernon Mauery     auto bufferSize = sol::Manager::get().dataBuffer.size();
20275e15db4STom Joseph 
20375e15db4STom Joseph     /* Sent a ACK only response */
20475e15db4STom Joseph     if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
20575e15db4STom Joseph     {
2066f353e86SVernon Mauery         enableAccumulateTimer(true);
20775e15db4STom Joseph 
20870fd29cfSVernon Mauery         std::vector<uint8_t> outPayload(sizeof(Payload));
20975e15db4STom Joseph         auto response = reinterpret_cast<Payload*>(outPayload.data());
21075e15db4STom Joseph         response->packetSeqNum = 0;
21175e15db4STom Joseph         response->packetAckSeqNum = ackSeqNum;
21275e15db4STom Joseph         response->acceptedCharCount = count;
21375e15db4STom Joseph         response->outOperation.ack = ack;
21475e15db4STom Joseph         sendPayload(outPayload);
21575e15db4STom Joseph         return;
21675e15db4STom Joseph     }
21775e15db4STom Joseph 
21875e15db4STom Joseph     auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
21975e15db4STom Joseph     payloadCache.resize(sizeof(Payload) + readSize);
22075e15db4STom Joseph     auto response = reinterpret_cast<Payload*>(payloadCache.data());
22175e15db4STom Joseph     response->packetAckSeqNum = ackSeqNum;
22275e15db4STom Joseph     response->acceptedCharCount = count;
22375e15db4STom Joseph     response->outOperation.ack = ack;
22475e15db4STom Joseph     response->packetSeqNum = seqNums.incOutboundSeqNum();
22575e15db4STom Joseph 
2262085ae07SVernon Mauery     auto handle = sol::Manager::get().dataBuffer.read();
22775e15db4STom Joseph     std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
22875e15db4STom Joseph     expectedCharCount = readSize;
22975e15db4STom Joseph 
2306f353e86SVernon Mauery     enableRetryTimer(true);
2316f353e86SVernon Mauery     enableAccumulateTimer(false);
23275e15db4STom Joseph 
23375e15db4STom Joseph     sendPayload(payloadCache);
23475e15db4STom Joseph }
23575e15db4STom Joseph 
2367306314fSTom Joseph int Context::sendOutboundPayload()
2377306314fSTom Joseph {
2387306314fSTom Joseph     if (payloadCache.size() != 0)
2397306314fSTom Joseph     {
2406f353e86SVernon Mauery         enableAccumulateTimer(true);
2417306314fSTom Joseph         return -1;
2427306314fSTom Joseph     }
2437306314fSTom Joseph 
2442085ae07SVernon Mauery     auto bufferSize = sol::Manager::get().dataBuffer.size();
2457306314fSTom Joseph     auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
2467306314fSTom Joseph 
2477306314fSTom Joseph     payloadCache.resize(sizeof(Payload) + readSize);
2487306314fSTom Joseph     auto response = reinterpret_cast<Payload*>(payloadCache.data());
2497306314fSTom Joseph     response->packetAckSeqNum = 0;
2507306314fSTom Joseph     response->acceptedCharCount = 0;
2517306314fSTom Joseph     response->outOperation.ack = false;
2527306314fSTom Joseph     response->packetSeqNum = seqNums.incOutboundSeqNum();
2537306314fSTom Joseph 
2542085ae07SVernon Mauery     auto handle = sol::Manager::get().dataBuffer.read();
2557306314fSTom Joseph     std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
2567306314fSTom Joseph     expectedCharCount = readSize;
2577306314fSTom Joseph 
2586f353e86SVernon Mauery     enableRetryTimer(true);
2596f353e86SVernon Mauery     enableAccumulateTimer(false);
2607306314fSTom Joseph 
2617306314fSTom Joseph     sendPayload(payloadCache);
2627306314fSTom Joseph 
2637306314fSTom Joseph     return 0;
2647306314fSTom Joseph }
2657306314fSTom Joseph 
26675e15db4STom Joseph void Context::resendPayload(bool clear)
267fbcac2e7STom Joseph {
2682fd466f4STom Joseph     sendPayload(payloadCache);
269fbcac2e7STom Joseph 
2702fd466f4STom Joseph     if (clear)
2712fd466f4STom Joseph     {
2722fd466f4STom Joseph         payloadCache.clear();
2732fd466f4STom Joseph         expectedCharCount = 0;
2742085ae07SVernon Mauery         sol::Manager::get().dataBuffer.erase(expectedCharCount);
2752fd466f4STom Joseph     }
276fbcac2e7STom Joseph }
277fbcac2e7STom Joseph 
27870fd29cfSVernon Mauery void Context::sendPayload(const std::vector<uint8_t>& out) const
279fbcac2e7STom Joseph {
28022596f21STom Joseph     message::Handler msgHandler(session->channelPtr, sessionID);
28122596f21STom Joseph 
28222596f21STom Joseph     msgHandler.sendSOLPayload(out);
283fbcac2e7STom Joseph }
284fbcac2e7STom Joseph 
2856f353e86SVernon Mauery void Context::charAccTimerHandler()
2866f353e86SVernon Mauery {
2872085ae07SVernon Mauery     auto bufferSize = sol::Manager::get().dataBuffer.size();
2886f353e86SVernon Mauery 
2896f353e86SVernon Mauery     try
2906f353e86SVernon Mauery     {
2916f353e86SVernon Mauery         if (bufferSize > 0)
2926f353e86SVernon Mauery         {
2936f353e86SVernon Mauery             int rc = sendOutboundPayload();
2946f353e86SVernon Mauery             if (rc == 0)
2956f353e86SVernon Mauery             {
2966f353e86SVernon Mauery                 return;
2976f353e86SVernon Mauery             }
2986f353e86SVernon Mauery         }
2996f353e86SVernon Mauery         enableAccumulateTimer(true);
3006f353e86SVernon Mauery     }
30112d199b2SPatrick Williams     catch (const std::exception& e)
3026f353e86SVernon Mauery     {
3037b7f25f7SGeorge Liu         lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
3047b7f25f7SGeorge Liu                    "ERROR", e);
3056f353e86SVernon Mauery     }
3066f353e86SVernon Mauery }
3076f353e86SVernon Mauery 
3086f353e86SVernon Mauery void Context::retryTimerHandler()
3096f353e86SVernon Mauery {
3106f353e86SVernon Mauery     try
3116f353e86SVernon Mauery     {
3126f353e86SVernon Mauery         if (retryCounter)
3136f353e86SVernon Mauery         {
3146f353e86SVernon Mauery             --retryCounter;
3156f353e86SVernon Mauery             enableRetryTimer(true);
3166f353e86SVernon Mauery             resendPayload(sol::Context::noClear);
3176f353e86SVernon Mauery         }
3186f353e86SVernon Mauery         else
3196f353e86SVernon Mauery         {
3206f353e86SVernon Mauery             retryCounter = maxRetryCount;
3216f353e86SVernon Mauery             resendPayload(sol::Context::clear);
3226f353e86SVernon Mauery             enableRetryTimer(false);
3236f353e86SVernon Mauery             enableAccumulateTimer(true);
3246f353e86SVernon Mauery         }
3256f353e86SVernon Mauery     }
32612d199b2SPatrick Williams     catch (const std::exception& e)
3276f353e86SVernon Mauery     {
3287b7f25f7SGeorge Liu         lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
3296f353e86SVernon Mauery     }
3306f353e86SVernon Mauery }
331fbcac2e7STom Joseph } // namespace sol
332