blob: 4428c9c158fe6ad7201d1d44a14a35cfbfe4174e [file] [log] [blame]
Vernon Mauery9e801a22018-10-12 13:20:49 -07001#include "sol_context.hpp"
2
Tom Josephfbcac2e2017-03-14 18:15:07 +05303#include "main.hpp"
Vernon Mauery2085ae02021-06-10 11:51:00 -07004#include "message_handler.hpp"
Tom Josephfbcac2e2017-03-14 18:15:07 +05305#include "sd_event_loop.hpp"
Vernon Mauery2085ae02021-06-10 11:51:00 -07006#include "sessions_manager.hpp"
Tom Josephfbcac2e2017-03-14 18:15:07 +05307#include "sol_manager.hpp"
8
Vernon Mauery9e801a22018-10-12 13:20:49 -07009#include <phosphor-logging/log.hpp>
10
Tom Josephfbcac2e2017-03-14 18:15:07 +053011namespace sol
12{
Tom Josephfbcac2e2017-03-14 18:15:07 +053013using namespace phosphor::logging;
14
Vernon Mauery6f353e82018-11-09 08:43:24 -080015Context::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{
Vernon Mauery2085ae02021-06-10 11:51:00 -070023 session = session::Manager::get().getSession(sessionID);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080024}
25
26std::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;
Vernon Mauery6f353e82018-11-09 08:43:24 -080035}
36
37void Context::enableAccumulateTimer(bool enable)
38{
39 // fetch the timeout from the SOL manager
Vernon Mauery2085ae02021-06-10 11:51:00 -070040 std::chrono::microseconds interval = sol::Manager::get().accumulateInterval;
Vernon Mauery6f353e82018-11-09 08:43:24 -080041 if (enable)
42 {
43 accumulateTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080044 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 });
Vernon Mauery6f353e82018-11-09 08:43:24 -080053 }
54 else
55 {
56 accumulateTimer.cancel();
57 }
58}
59
60void Context::enableRetryTimer(bool enable)
61{
62 if (enable)
63 {
64 // fetch the timeout from the SOL manager
Vernon Mauery2085ae02021-06-10 11:51:00 -070065 std::chrono::microseconds interval = sol::Manager::get().retryInterval;
Vernon Mauery6f353e82018-11-09 08:43:24 -080066 retryTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080067 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)
Vernon Mauery6f353e82018-11-09 08:43:24 -080071 {
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080072 self->retryTimerHandler();
Vernon Mauery6f353e82018-11-09 08:43:24 -080073 }
74 });
75 }
76 else
77 {
78 retryTimer.cancel();
79 }
80}
81
Vernon Mauery9e801a22018-10-12 13:20:49 -070082void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
83 uint8_t count, bool status,
Vernon Mauery70fd29c2017-11-30 13:11:43 -080084 const std::vector<uint8_t>& input)
Tom Josephfbcac2e2017-03-14 18:15:07 +053085{
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 */
Vernon Mauery9e801a22018-10-12 13:20:49 -070097 if (seqNum && (seqNum != seqNums.get(true)))
Tom Josephfbcac2e2017-03-14 18:15:07 +053098 {
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);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800125 enableRetryTimer(false);
126 enableRetryTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530127 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
Vernon Mauery2085ae02021-06-10 11:51:00 -0700136 sol::Manager::get().dataBuffer.erase(count);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530137
138 // Once it is acknowledged stop the retry interval timer
Vernon Mauery6f353e82018-11-09 08:43:24 -0800139 enableRetryTimer(false);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530140
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 {
Vernon Mauery2085ae02021-06-10 11:51:00 -0700149 auto rc = sol::Manager::get().writeConsoleSocket(input);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530150 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 }
Tom Joseph694fc0c2017-08-21 11:47:25 +0530162 /*
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 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530170
171 if (seqNum != 0)
172 {
173 seqNums.incInboundSeqNum();
174 prepareResponse(respAckSeqNum, acceptedCount, ack);
175 }
176 else
177 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800178 enableAccumulateTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530179 }
180}
181
Tom Joseph75e15db2017-03-14 18:16:22 +0530182void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
183{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700184 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph75e15db2017-03-14 18:16:22 +0530185
186 /* Sent a ACK only response */
187 if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
188 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800189 enableAccumulateTimer(true);
Tom Joseph75e15db2017-03-14 18:16:22 +0530190
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800191 std::vector<uint8_t> outPayload(sizeof(Payload));
Tom Joseph75e15db2017-03-14 18:16:22 +0530192 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
Vernon Mauery2085ae02021-06-10 11:51:00 -0700209 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph75e15db2017-03-14 18:16:22 +0530210 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
211 expectedCharCount = readSize;
212
Vernon Mauery6f353e82018-11-09 08:43:24 -0800213 enableRetryTimer(true);
214 enableAccumulateTimer(false);
Tom Joseph75e15db2017-03-14 18:16:22 +0530215
216 sendPayload(payloadCache);
217}
218
Tom Joseph73063142017-03-14 18:20:20 +0530219int Context::sendOutboundPayload()
220{
221 if (payloadCache.size() != 0)
222 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800223 enableAccumulateTimer(true);
Tom Joseph73063142017-03-14 18:20:20 +0530224 return -1;
225 }
226
Vernon Mauery2085ae02021-06-10 11:51:00 -0700227 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph73063142017-03-14 18:20:20 +0530228 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
Vernon Mauery2085ae02021-06-10 11:51:00 -0700237 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph73063142017-03-14 18:20:20 +0530238 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
239 expectedCharCount = readSize;
240
Vernon Mauery6f353e82018-11-09 08:43:24 -0800241 enableRetryTimer(true);
242 enableAccumulateTimer(false);
Tom Joseph73063142017-03-14 18:20:20 +0530243
244 sendPayload(payloadCache);
245
246 return 0;
247}
248
Tom Joseph75e15db2017-03-14 18:16:22 +0530249void Context::resendPayload(bool clear)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530250{
Tom Joseph2fd466f2017-03-30 08:16:47 +0530251 sendPayload(payloadCache);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530252
Vernon Mauery9e801a22018-10-12 13:20:49 -0700253 if (clear)
Tom Joseph2fd466f2017-03-30 08:16:47 +0530254 {
255 payloadCache.clear();
256 expectedCharCount = 0;
Vernon Mauery2085ae02021-06-10 11:51:00 -0700257 sol::Manager::get().dataBuffer.erase(expectedCharCount);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530258 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530259}
260
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800261void Context::sendPayload(const std::vector<uint8_t>& out) const
Tom Josephfbcac2e2017-03-14 18:15:07 +0530262{
Tom Joseph22596f22017-03-31 10:52:27 +0530263 message::Handler msgHandler(session->channelPtr, sessionID);
264
265 msgHandler.sendSOLPayload(out);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530266}
267
Vernon Mauery6f353e82018-11-09 08:43:24 -0800268void Context::charAccTimerHandler()
269{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700270 auto bufferSize = sol::Manager::get().dataBuffer.size();
Vernon Mauery6f353e82018-11-09 08:43:24 -0800271
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 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500284 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800285 {
286 log<level::ERR>(e.what());
287 }
288}
289
290void 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 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500308 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800309 {
310 log<level::ERR>(e.what());
311 }
312}
Tom Josephfbcac2e2017-03-14 18:15:07 +0530313} // namespace sol