blob: 7165c61c32d3d30fba0ea651fc0229339a0fbb72 [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 {
Konstantin Aladyshev426fcab2024-03-13 17:56:50 +030049 try
50 {
51 int rc = sendOutboundPayload();
52 if (rc == 0)
Jian Zhang5499bf82024-01-24 15:33:11 +080053 {
Konstantin Aladyshev426fcab2024-03-13 17:56:50 +030054 return;
Jian Zhang5499bf82024-01-24 15:33:11 +080055 }
Konstantin Aladyshev426fcab2024-03-13 17:56:50 +030056 }
57 catch (const std::exception& e)
58 {
59 lg2::error(
60 "Failed to call the sendOutboundPayload method: {ERROR}",
61 "ERROR", e);
62 return;
63 }
Tang Yiwei15535142022-09-21 17:28:15 +080064 }
Vernon Mauery6f353e82018-11-09 08:43:24 -080065 accumulateTimer.expires_after(interval);
Konstantin Aladyshev426fcab2024-03-13 17:56:50 +030066 std::weak_ptr<Context> weakRef = weak_from_this();
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080067 accumulateTimer.async_wait(
68 [weakRef](const boost::system::error_code& ec) {
Patrick Williams099fb092023-05-10 07:50:31 -050069 std::shared_ptr<Context> self = weakRef.lock();
70 if (!ec && self)
71 {
72 self->charAccTimerHandler();
73 }
74 });
Vernon Mauery6f353e82018-11-09 08:43:24 -080075 }
76 else
77 {
78 accumulateTimer.cancel();
79 }
80}
81
82void Context::enableRetryTimer(bool enable)
83{
84 if (enable)
85 {
86 // fetch the timeout from the SOL manager
Vernon Mauery2085ae02021-06-10 11:51:00 -070087 std::chrono::microseconds interval = sol::Manager::get().retryInterval;
Vernon Mauery6f353e82018-11-09 08:43:24 -080088 retryTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080089 std::weak_ptr<Context> weakRef = weak_from_this();
90 retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
91 std::shared_ptr<Context> self = weakRef.lock();
92 if (!ec && self)
Vernon Mauery6f353e82018-11-09 08:43:24 -080093 {
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080094 self->retryTimerHandler();
Vernon Mauery6f353e82018-11-09 08:43:24 -080095 }
96 });
97 }
98 else
99 {
100 retryTimer.cancel();
101 }
102}
103
Vernon Mauery9e801a22018-10-12 13:20:49 -0700104void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
Tingting Chenec437412022-09-27 18:35:22 +0800105 uint8_t count, bool status, bool isBreak,
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800106 const std::vector<uint8_t>& input)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530107{
108 uint8_t respAckSeqNum = 0;
109 uint8_t acceptedCount = 0;
110 auto ack = false;
111
112 /*
113 * Check if the Inbound sequence number is same as the expected one.
114 * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
115 * outstanding sequence numbers are not supported in this version of the SOL
116 * specification. Retried packets use the same sequence number as the first
117 * packet.
118 */
Vernon Mauery9e801a22018-10-12 13:20:49 -0700119 if (seqNum && (seqNum != seqNums.get(true)))
Tom Josephfbcac2e2017-03-14 18:15:07 +0530120 {
George Liu7b7f25f2022-07-04 17:07:32 +0800121 lg2::info("Out of sequence SOL packet - packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530122 return;
123 }
124
125 /*
126 * Check if the expected ACK/NACK sequence number is same as the
127 * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
128 * number is 0, then it is an informational packet. No request packet being
129 * ACK'd or NACK'd.
130 */
131 if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
132 {
George Liu7b7f25f2022-07-04 17:07:32 +0800133 lg2::info("Out of sequence ack number - SOL packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530134 return;
135 }
136
137 /*
138 * Retry the SOL payload packet in the following conditions:
139 *
140 * a) NACK in Operation/Status
141 * b) Accepted Character Count does not match with the sent out SOL payload
142 * c) Non-zero Packet ACK/NACK Sequence Number
143 */
144 if (status || ((count != expectedCharCount) && ackSeqNum))
145 {
146 resendPayload(noClear);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800147 enableRetryTimer(false);
148 enableRetryTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530149 return;
150 }
151 /*
152 * Clear the sent data once the acknowledgment sequence number matches
153 * and the expected character count matches.
154 */
155 else if ((count == expectedCharCount) && ackSeqNum)
156 {
157 // Clear the Host Console Buffer
Vernon Mauery2085ae02021-06-10 11:51:00 -0700158 sol::Manager::get().dataBuffer.erase(count);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530159
160 // Once it is acknowledged stop the retry interval timer
Vernon Mauery6f353e82018-11-09 08:43:24 -0800161 enableRetryTimer(false);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530162
163 retryCounter = maxRetryCount;
164 expectedCharCount = 0;
165 payloadCache.clear();
166 }
167
Tingting Chenec437412022-09-27 18:35:22 +0800168 if (isBreak && seqNum)
169 {
170 lg2::info("Writing break to console socket descriptor");
171 constexpr uint8_t sysrqValue = 72; // use this to notify sol server
172 const std::vector<uint8_t> test{sysrqValue};
173 auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
174 if (ret)
175 {
176 lg2::error("Writing to console socket descriptor failed: {ERROR}",
177 "ERROR", strerror(errno));
178 }
179 }
180
181 isBreak = false;
Tom Josephfbcac2e2017-03-14 18:15:07 +0530182 // Write character data to the Host Console
183 if (!input.empty() && seqNum)
184 {
Tingting Chenec437412022-09-27 18:35:22 +0800185 auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530186 if (rc)
187 {
George Liu7b7f25f2022-07-04 17:07:32 +0800188 lg2::error("Writing to console socket descriptor failed: {ERROR}",
189 "ERROR", strerror(errno));
Tom Josephfbcac2e2017-03-14 18:15:07 +0530190 ack = true;
191 }
192 else
193 {
194 respAckSeqNum = seqNum;
195 ack = false;
196 acceptedCount = input.size();
197 }
198 }
Tom Joseph694fc0c2017-08-21 11:47:25 +0530199 /*
200 * SOL payload with no character data and valid sequence number can be used
201 * as method to keep the SOL session active.
202 */
203 else if (input.empty() && seqNum)
204 {
205 respAckSeqNum = seqNum;
206 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530207
208 if (seqNum != 0)
209 {
210 seqNums.incInboundSeqNum();
211 prepareResponse(respAckSeqNum, acceptedCount, ack);
212 }
213 else
214 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800215 enableAccumulateTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530216 }
217}
218
Tom Joseph75e15db2017-03-14 18:16:22 +0530219void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
220{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700221 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph75e15db2017-03-14 18:16:22 +0530222
223 /* Sent a ACK only response */
224 if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
225 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800226 enableAccumulateTimer(true);
Tom Joseph75e15db2017-03-14 18:16:22 +0530227
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800228 std::vector<uint8_t> outPayload(sizeof(Payload));
Tom Joseph75e15db2017-03-14 18:16:22 +0530229 auto response = reinterpret_cast<Payload*>(outPayload.data());
230 response->packetSeqNum = 0;
231 response->packetAckSeqNum = ackSeqNum;
232 response->acceptedCharCount = count;
233 response->outOperation.ack = ack;
234 sendPayload(outPayload);
235 return;
236 }
237
238 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
239 payloadCache.resize(sizeof(Payload) + readSize);
240 auto response = reinterpret_cast<Payload*>(payloadCache.data());
241 response->packetAckSeqNum = ackSeqNum;
242 response->acceptedCharCount = count;
243 response->outOperation.ack = ack;
244 response->packetSeqNum = seqNums.incOutboundSeqNum();
245
Vernon Mauery2085ae02021-06-10 11:51:00 -0700246 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph75e15db2017-03-14 18:16:22 +0530247 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
248 expectedCharCount = readSize;
249
Vernon Mauery6f353e82018-11-09 08:43:24 -0800250 enableRetryTimer(true);
251 enableAccumulateTimer(false);
Tom Joseph75e15db2017-03-14 18:16:22 +0530252
253 sendPayload(payloadCache);
254}
255
Tom Joseph73063142017-03-14 18:20:20 +0530256int Context::sendOutboundPayload()
257{
258 if (payloadCache.size() != 0)
259 {
Tom Joseph73063142017-03-14 18:20:20 +0530260 return -1;
261 }
262
Vernon Mauery2085ae02021-06-10 11:51:00 -0700263 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph73063142017-03-14 18:20:20 +0530264 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
265
266 payloadCache.resize(sizeof(Payload) + readSize);
267 auto response = reinterpret_cast<Payload*>(payloadCache.data());
268 response->packetAckSeqNum = 0;
269 response->acceptedCharCount = 0;
270 response->outOperation.ack = false;
271 response->packetSeqNum = seqNums.incOutboundSeqNum();
272
Vernon Mauery2085ae02021-06-10 11:51:00 -0700273 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph73063142017-03-14 18:20:20 +0530274 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
275 expectedCharCount = readSize;
276
Vernon Mauery6f353e82018-11-09 08:43:24 -0800277 enableRetryTimer(true);
278 enableAccumulateTimer(false);
Tom Joseph73063142017-03-14 18:20:20 +0530279
280 sendPayload(payloadCache);
281
282 return 0;
283}
284
Tom Joseph75e15db2017-03-14 18:16:22 +0530285void Context::resendPayload(bool clear)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530286{
Tom Joseph2fd466f2017-03-30 08:16:47 +0530287 sendPayload(payloadCache);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530288
Vernon Mauery9e801a22018-10-12 13:20:49 -0700289 if (clear)
Tom Joseph2fd466f2017-03-30 08:16:47 +0530290 {
291 payloadCache.clear();
292 expectedCharCount = 0;
Vernon Mauery2085ae02021-06-10 11:51:00 -0700293 sol::Manager::get().dataBuffer.erase(expectedCharCount);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530294 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530295}
296
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800297void Context::sendPayload(const std::vector<uint8_t>& out) const
Tom Josephfbcac2e2017-03-14 18:15:07 +0530298{
Tom Joseph22596f22017-03-31 10:52:27 +0530299 message::Handler msgHandler(session->channelPtr, sessionID);
300
301 msgHandler.sendSOLPayload(out);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530302}
303
Vernon Mauery6f353e82018-11-09 08:43:24 -0800304void Context::charAccTimerHandler()
305{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700306 auto bufferSize = sol::Manager::get().dataBuffer.size();
Vernon Mauery6f353e82018-11-09 08:43:24 -0800307
308 try
309 {
310 if (bufferSize > 0)
311 {
312 int rc = sendOutboundPayload();
313 if (rc == 0)
314 {
315 return;
316 }
317 }
318 enableAccumulateTimer(true);
319 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500320 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800321 {
George Liu7b7f25f2022-07-04 17:07:32 +0800322 lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
323 "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800324 }
325}
326
327void Context::retryTimerHandler()
328{
329 try
330 {
331 if (retryCounter)
332 {
333 --retryCounter;
334 enableRetryTimer(true);
335 resendPayload(sol::Context::noClear);
336 }
337 else
338 {
339 retryCounter = maxRetryCount;
340 resendPayload(sol::Context::clear);
341 enableRetryTimer(false);
342 enableAccumulateTimer(true);
343 }
344 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500345 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800346 {
George Liu7b7f25f2022-07-04 17:07:32 +0800347 lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800348 }
349}
Tom Josephfbcac2e2017-03-14 18:15:07 +0530350} // namespace sol