blob: 7bae1cb0232ad303bd54558d37787421ee11ab94 [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
George Liu7b7f25f2022-07-04 17:07:32 +08009#include <errno.h>
10
11#include <phosphor-logging/lg2.hpp>
Vernon Mauery9e801a22018-10-12 13:20:49 -070012
Tom Josephfbcac2e2017-03-14 18:15:07 +053013namespace sol
14{
Tom Josephfbcac2e2017-03-14 18:15:07 +053015using namespace phosphor::logging;
16
Vernon Mauery6f353e82018-11-09 08:43:24 -080017Context::Context(std::shared_ptr<boost::asio::io_context> io,
18 uint8_t maxRetryCount, uint8_t sendThreshold, uint8_t instance,
19 session::SessionID sessionID) :
20 accumulateTimer(*io),
21 retryTimer(*io), maxRetryCount(maxRetryCount), retryCounter(maxRetryCount),
22 sendThreshold(sendThreshold), payloadInstance(instance),
23 sessionID(sessionID)
24{
Vernon Mauery2085ae02021-06-10 11:51:00 -070025 session = session::Manager::get().getSession(sessionID);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080026}
27
28std::shared_ptr<Context>
29 Context::makeContext(std::shared_ptr<boost::asio::io_context> io,
30 uint8_t maxRetryCount, uint8_t sendThreshold,
31 uint8_t instance, session::SessionID sessionID)
32{
33 auto ctx = std::make_shared<Context>(io, maxRetryCount, sendThreshold,
34 instance, sessionID);
35 ctx->enableAccumulateTimer(true);
36 return ctx;
Vernon Mauery6f353e82018-11-09 08:43:24 -080037}
38
39void Context::enableAccumulateTimer(bool enable)
40{
41 // fetch the timeout from the SOL manager
Vernon Mauery2085ae02021-06-10 11:51:00 -070042 std::chrono::microseconds interval = sol::Manager::get().accumulateInterval;
Tang Yiwei15535142022-09-21 17:28:15 +080043
Vernon Mauery6f353e82018-11-09 08:43:24 -080044 if (enable)
45 {
Tang Yiwei15535142022-09-21 17:28:15 +080046 auto bufferSize = sol::Manager::get().dataBuffer.size();
47 if (bufferSize > sendThreshold)
48 {
49 charAccTimerHandler();
50 return;
51 }
Vernon Mauery6f353e82018-11-09 08:43:24 -080052 accumulateTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080053 std::weak_ptr<Context> weakRef = weak_from_this();
54 accumulateTimer.async_wait(
55 [weakRef](const boost::system::error_code& ec) {
Patrick Williams099fb092023-05-10 07:50:31 -050056 std::shared_ptr<Context> self = weakRef.lock();
57 if (!ec && self)
58 {
59 self->charAccTimerHandler();
60 }
61 });
Vernon Mauery6f353e82018-11-09 08:43:24 -080062 }
63 else
64 {
65 accumulateTimer.cancel();
66 }
67}
68
69void Context::enableRetryTimer(bool enable)
70{
71 if (enable)
72 {
73 // fetch the timeout from the SOL manager
Vernon Mauery2085ae02021-06-10 11:51:00 -070074 std::chrono::microseconds interval = sol::Manager::get().retryInterval;
Vernon Mauery6f353e82018-11-09 08:43:24 -080075 retryTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080076 std::weak_ptr<Context> weakRef = weak_from_this();
77 retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
78 std::shared_ptr<Context> self = weakRef.lock();
79 if (!ec && self)
Vernon Mauery6f353e82018-11-09 08:43:24 -080080 {
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080081 self->retryTimerHandler();
Vernon Mauery6f353e82018-11-09 08:43:24 -080082 }
83 });
84 }
85 else
86 {
87 retryTimer.cancel();
88 }
89}
90
Vernon Mauery9e801a22018-10-12 13:20:49 -070091void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
Tingting Chenec437412022-09-27 18:35:22 +080092 uint8_t count, bool status, bool isBreak,
Vernon Mauery70fd29c2017-11-30 13:11:43 -080093 const std::vector<uint8_t>& input)
Tom Josephfbcac2e2017-03-14 18:15:07 +053094{
95 uint8_t respAckSeqNum = 0;
96 uint8_t acceptedCount = 0;
97 auto ack = false;
98
99 /*
100 * Check if the Inbound sequence number is same as the expected one.
101 * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
102 * outstanding sequence numbers are not supported in this version of the SOL
103 * specification. Retried packets use the same sequence number as the first
104 * packet.
105 */
Vernon Mauery9e801a22018-10-12 13:20:49 -0700106 if (seqNum && (seqNum != seqNums.get(true)))
Tom Josephfbcac2e2017-03-14 18:15:07 +0530107 {
George Liu7b7f25f2022-07-04 17:07:32 +0800108 lg2::info("Out of sequence SOL packet - packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530109 return;
110 }
111
112 /*
113 * Check if the expected ACK/NACK sequence number is same as the
114 * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
115 * number is 0, then it is an informational packet. No request packet being
116 * ACK'd or NACK'd.
117 */
118 if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
119 {
George Liu7b7f25f2022-07-04 17:07:32 +0800120 lg2::info("Out of sequence ack number - SOL packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530121 return;
122 }
123
124 /*
125 * Retry the SOL payload packet in the following conditions:
126 *
127 * a) NACK in Operation/Status
128 * b) Accepted Character Count does not match with the sent out SOL payload
129 * c) Non-zero Packet ACK/NACK Sequence Number
130 */
131 if (status || ((count != expectedCharCount) && ackSeqNum))
132 {
133 resendPayload(noClear);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800134 enableRetryTimer(false);
135 enableRetryTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530136 return;
137 }
138 /*
139 * Clear the sent data once the acknowledgment sequence number matches
140 * and the expected character count matches.
141 */
142 else if ((count == expectedCharCount) && ackSeqNum)
143 {
144 // Clear the Host Console Buffer
Vernon Mauery2085ae02021-06-10 11:51:00 -0700145 sol::Manager::get().dataBuffer.erase(count);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530146
147 // Once it is acknowledged stop the retry interval timer
Vernon Mauery6f353e82018-11-09 08:43:24 -0800148 enableRetryTimer(false);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530149
150 retryCounter = maxRetryCount;
151 expectedCharCount = 0;
152 payloadCache.clear();
153 }
154
Tingting Chenec437412022-09-27 18:35:22 +0800155 if (isBreak && seqNum)
156 {
157 lg2::info("Writing break to console socket descriptor");
158 constexpr uint8_t sysrqValue = 72; // use this to notify sol server
159 const std::vector<uint8_t> test{sysrqValue};
160 auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
161 if (ret)
162 {
163 lg2::error("Writing to console socket descriptor failed: {ERROR}",
164 "ERROR", strerror(errno));
165 }
166 }
167
168 isBreak = false;
Tom Josephfbcac2e2017-03-14 18:15:07 +0530169 // Write character data to the Host Console
170 if (!input.empty() && seqNum)
171 {
Tingting Chenec437412022-09-27 18:35:22 +0800172 auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530173 if (rc)
174 {
George Liu7b7f25f2022-07-04 17:07:32 +0800175 lg2::error("Writing to console socket descriptor failed: {ERROR}",
176 "ERROR", strerror(errno));
Tom Josephfbcac2e2017-03-14 18:15:07 +0530177 ack = true;
178 }
179 else
180 {
181 respAckSeqNum = seqNum;
182 ack = false;
183 acceptedCount = input.size();
184 }
185 }
Tom Joseph694fc0c2017-08-21 11:47:25 +0530186 /*
187 * SOL payload with no character data and valid sequence number can be used
188 * as method to keep the SOL session active.
189 */
190 else if (input.empty() && seqNum)
191 {
192 respAckSeqNum = seqNum;
193 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530194
195 if (seqNum != 0)
196 {
197 seqNums.incInboundSeqNum();
198 prepareResponse(respAckSeqNum, acceptedCount, ack);
199 }
200 else
201 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800202 enableAccumulateTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530203 }
204}
205
Tom Joseph75e15db2017-03-14 18:16:22 +0530206void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
207{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700208 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph75e15db2017-03-14 18:16:22 +0530209
210 /* Sent a ACK only response */
211 if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
212 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800213 enableAccumulateTimer(true);
Tom Joseph75e15db2017-03-14 18:16:22 +0530214
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800215 std::vector<uint8_t> outPayload(sizeof(Payload));
Tom Joseph75e15db2017-03-14 18:16:22 +0530216 auto response = reinterpret_cast<Payload*>(outPayload.data());
217 response->packetSeqNum = 0;
218 response->packetAckSeqNum = ackSeqNum;
219 response->acceptedCharCount = count;
220 response->outOperation.ack = ack;
221 sendPayload(outPayload);
222 return;
223 }
224
225 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
226 payloadCache.resize(sizeof(Payload) + readSize);
227 auto response = reinterpret_cast<Payload*>(payloadCache.data());
228 response->packetAckSeqNum = ackSeqNum;
229 response->acceptedCharCount = count;
230 response->outOperation.ack = ack;
231 response->packetSeqNum = seqNums.incOutboundSeqNum();
232
Vernon Mauery2085ae02021-06-10 11:51:00 -0700233 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph75e15db2017-03-14 18:16:22 +0530234 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
235 expectedCharCount = readSize;
236
Vernon Mauery6f353e82018-11-09 08:43:24 -0800237 enableRetryTimer(true);
238 enableAccumulateTimer(false);
Tom Joseph75e15db2017-03-14 18:16:22 +0530239
240 sendPayload(payloadCache);
241}
242
Tom Joseph73063142017-03-14 18:20:20 +0530243int Context::sendOutboundPayload()
244{
245 if (payloadCache.size() != 0)
246 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800247 enableAccumulateTimer(true);
Tom Joseph73063142017-03-14 18:20:20 +0530248 return -1;
249 }
250
Vernon Mauery2085ae02021-06-10 11:51:00 -0700251 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph73063142017-03-14 18:20:20 +0530252 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
253
254 payloadCache.resize(sizeof(Payload) + readSize);
255 auto response = reinterpret_cast<Payload*>(payloadCache.data());
256 response->packetAckSeqNum = 0;
257 response->acceptedCharCount = 0;
258 response->outOperation.ack = false;
259 response->packetSeqNum = seqNums.incOutboundSeqNum();
260
Vernon Mauery2085ae02021-06-10 11:51:00 -0700261 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph73063142017-03-14 18:20:20 +0530262 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
263 expectedCharCount = readSize;
264
Vernon Mauery6f353e82018-11-09 08:43:24 -0800265 enableRetryTimer(true);
266 enableAccumulateTimer(false);
Tom Joseph73063142017-03-14 18:20:20 +0530267
268 sendPayload(payloadCache);
269
270 return 0;
271}
272
Tom Joseph75e15db2017-03-14 18:16:22 +0530273void Context::resendPayload(bool clear)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530274{
Tom Joseph2fd466f2017-03-30 08:16:47 +0530275 sendPayload(payloadCache);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530276
Vernon Mauery9e801a22018-10-12 13:20:49 -0700277 if (clear)
Tom Joseph2fd466f2017-03-30 08:16:47 +0530278 {
279 payloadCache.clear();
280 expectedCharCount = 0;
Vernon Mauery2085ae02021-06-10 11:51:00 -0700281 sol::Manager::get().dataBuffer.erase(expectedCharCount);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530282 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530283}
284
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800285void Context::sendPayload(const std::vector<uint8_t>& out) const
Tom Josephfbcac2e2017-03-14 18:15:07 +0530286{
Tom Joseph22596f22017-03-31 10:52:27 +0530287 message::Handler msgHandler(session->channelPtr, sessionID);
288
289 msgHandler.sendSOLPayload(out);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530290}
291
Vernon Mauery6f353e82018-11-09 08:43:24 -0800292void Context::charAccTimerHandler()
293{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700294 auto bufferSize = sol::Manager::get().dataBuffer.size();
Vernon Mauery6f353e82018-11-09 08:43:24 -0800295
296 try
297 {
298 if (bufferSize > 0)
299 {
300 int rc = sendOutboundPayload();
301 if (rc == 0)
302 {
303 return;
304 }
305 }
306 enableAccumulateTimer(true);
307 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500308 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800309 {
George Liu7b7f25f2022-07-04 17:07:32 +0800310 lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
311 "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800312 }
313}
314
315void Context::retryTimerHandler()
316{
317 try
318 {
319 if (retryCounter)
320 {
321 --retryCounter;
322 enableRetryTimer(true);
323 resendPayload(sol::Context::noClear);
324 }
325 else
326 {
327 retryCounter = maxRetryCount;
328 resendPayload(sol::Context::clear);
329 enableRetryTimer(false);
330 enableAccumulateTimer(true);
331 }
332 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500333 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800334 {
George Liu7b7f25f2022-07-04 17:07:32 +0800335 lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800336 }
337}
Tom Josephfbcac2e2017-03-14 18:15:07 +0530338} // namespace sol