blob: 21632a47899c26eed84f7108dda9a600dabbbb28 [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) :
Patrick Williams84256242024-08-16 15:20:21 -040020 accumulateTimer(*io), retryTimer(*io), maxRetryCount(maxRetryCount),
21 retryCounter(maxRetryCount), sendThreshold(sendThreshold),
22 payloadInstance(instance), sessionID(sessionID)
Vernon Mauery6f353e82018-11-09 08:43:24 -080023{
Vernon Mauery2085ae02021-06-10 11:51:00 -070024 session = session::Manager::get().getSession(sessionID);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080025}
26
Patrick Williams84256242024-08-16 15:20:21 -040027std::shared_ptr<Context> Context::makeContext(
28 std::shared_ptr<boost::asio::io_context> io, uint8_t maxRetryCount,
29 uint8_t sendThreshold, uint8_t instance, session::SessionID sessionID)
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080030{
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;
Tang Yiwei15535142022-09-21 17:28:15 +080041
Vernon Mauery6f353e82018-11-09 08:43:24 -080042 if (enable)
43 {
Tang Yiwei15535142022-09-21 17:28:15 +080044 auto bufferSize = sol::Manager::get().dataBuffer.size();
45 if (bufferSize > sendThreshold)
46 {
Konstantin Aladyshev426fcab2024-03-13 17:56:50 +030047 try
48 {
49 int rc = sendOutboundPayload();
50 if (rc == 0)
Jian Zhang5499bf82024-01-24 15:33:11 +080051 {
Konstantin Aladyshev426fcab2024-03-13 17:56:50 +030052 return;
Jian Zhang5499bf82024-01-24 15:33:11 +080053 }
Konstantin Aladyshev426fcab2024-03-13 17:56:50 +030054 }
55 catch (const std::exception& e)
56 {
57 lg2::error(
58 "Failed to call the sendOutboundPayload method: {ERROR}",
59 "ERROR", e);
60 return;
61 }
Tang Yiwei15535142022-09-21 17:28:15 +080062 }
Vernon Mauery6f353e82018-11-09 08:43:24 -080063 accumulateTimer.expires_after(interval);
Konstantin Aladyshev426fcab2024-03-13 17:56:50 +030064 std::weak_ptr<Context> weakRef = weak_from_this();
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080065 accumulateTimer.async_wait(
66 [weakRef](const boost::system::error_code& ec) {
Patrick Williams84256242024-08-16 15:20:21 -040067 std::shared_ptr<Context> self = weakRef.lock();
68 if (!ec && self)
69 {
70 self->charAccTimerHandler();
71 }
72 });
Vernon Mauery6f353e82018-11-09 08:43:24 -080073 }
74 else
75 {
76 accumulateTimer.cancel();
77 }
78}
79
80void Context::enableRetryTimer(bool enable)
81{
82 if (enable)
83 {
84 // fetch the timeout from the SOL manager
Vernon Mauery2085ae02021-06-10 11:51:00 -070085 std::chrono::microseconds interval = sol::Manager::get().retryInterval;
Vernon Mauery6f353e82018-11-09 08:43:24 -080086 retryTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080087 std::weak_ptr<Context> weakRef = weak_from_this();
88 retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
89 std::shared_ptr<Context> self = weakRef.lock();
90 if (!ec && self)
Vernon Mauery6f353e82018-11-09 08:43:24 -080091 {
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080092 self->retryTimerHandler();
Vernon Mauery6f353e82018-11-09 08:43:24 -080093 }
94 });
95 }
96 else
97 {
98 retryTimer.cancel();
99 }
100}
101
Vernon Mauery9e801a22018-10-12 13:20:49 -0700102void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
Tingting Chenec437412022-09-27 18:35:22 +0800103 uint8_t count, bool status, bool isBreak,
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800104 const std::vector<uint8_t>& input)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530105{
106 uint8_t respAckSeqNum = 0;
107 uint8_t acceptedCount = 0;
108 auto ack = false;
109
110 /*
111 * Check if the Inbound sequence number is same as the expected one.
112 * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
113 * outstanding sequence numbers are not supported in this version of the SOL
114 * specification. Retried packets use the same sequence number as the first
115 * packet.
116 */
Vernon Mauery9e801a22018-10-12 13:20:49 -0700117 if (seqNum && (seqNum != seqNums.get(true)))
Tom Josephfbcac2e2017-03-14 18:15:07 +0530118 {
George Liu7b7f25f2022-07-04 17:07:32 +0800119 lg2::info("Out of sequence SOL packet - packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530120 return;
121 }
122
123 /*
124 * Check if the expected ACK/NACK sequence number is same as the
125 * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
126 * number is 0, then it is an informational packet. No request packet being
127 * ACK'd or NACK'd.
128 */
129 if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
130 {
George Liu7b7f25f2022-07-04 17:07:32 +0800131 lg2::info("Out of sequence ack number - SOL packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530132 return;
133 }
134
135 /*
136 * Retry the SOL payload packet in the following conditions:
137 *
138 * a) NACK in Operation/Status
139 * b) Accepted Character Count does not match with the sent out SOL payload
140 * c) Non-zero Packet ACK/NACK Sequence Number
141 */
142 if (status || ((count != expectedCharCount) && ackSeqNum))
143 {
144 resendPayload(noClear);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800145 enableRetryTimer(false);
146 enableRetryTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530147 return;
148 }
149 /*
150 * Clear the sent data once the acknowledgment sequence number matches
151 * and the expected character count matches.
152 */
153 else if ((count == expectedCharCount) && ackSeqNum)
154 {
155 // Clear the Host Console Buffer
Vernon Mauery2085ae02021-06-10 11:51:00 -0700156 sol::Manager::get().dataBuffer.erase(count);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530157
158 // Once it is acknowledged stop the retry interval timer
Vernon Mauery6f353e82018-11-09 08:43:24 -0800159 enableRetryTimer(false);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530160
161 retryCounter = maxRetryCount;
162 expectedCharCount = 0;
163 payloadCache.clear();
164 }
165
Tingting Chenec437412022-09-27 18:35:22 +0800166 if (isBreak && seqNum)
167 {
168 lg2::info("Writing break to console socket descriptor");
169 constexpr uint8_t sysrqValue = 72; // use this to notify sol server
170 const std::vector<uint8_t> test{sysrqValue};
171 auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
172 if (ret)
173 {
174 lg2::error("Writing to console socket descriptor failed: {ERROR}",
175 "ERROR", strerror(errno));
176 }
177 }
178
179 isBreak = false;
Tom Josephfbcac2e2017-03-14 18:15:07 +0530180 // Write character data to the Host Console
181 if (!input.empty() && seqNum)
182 {
Tingting Chenec437412022-09-27 18:35:22 +0800183 auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530184 if (rc)
185 {
George Liu7b7f25f2022-07-04 17:07:32 +0800186 lg2::error("Writing to console socket descriptor failed: {ERROR}",
187 "ERROR", strerror(errno));
Tom Josephfbcac2e2017-03-14 18:15:07 +0530188 ack = true;
189 }
190 else
191 {
192 respAckSeqNum = seqNum;
193 ack = false;
194 acceptedCount = input.size();
195 }
196 }
Tom Joseph694fc0c2017-08-21 11:47:25 +0530197 /*
198 * SOL payload with no character data and valid sequence number can be used
199 * as method to keep the SOL session active.
200 */
201 else if (input.empty() && seqNum)
202 {
203 respAckSeqNum = seqNum;
204 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530205
206 if (seqNum != 0)
207 {
208 seqNums.incInboundSeqNum();
209 prepareResponse(respAckSeqNum, acceptedCount, ack);
210 }
211 else
212 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800213 enableAccumulateTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530214 }
215}
216
Tom Joseph75e15db2017-03-14 18:16:22 +0530217void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
218{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700219 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph75e15db2017-03-14 18:16:22 +0530220
221 /* Sent a ACK only response */
222 if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
223 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800224 enableAccumulateTimer(true);
Tom Joseph75e15db2017-03-14 18:16:22 +0530225
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800226 std::vector<uint8_t> outPayload(sizeof(Payload));
Tom Joseph75e15db2017-03-14 18:16:22 +0530227 auto response = reinterpret_cast<Payload*>(outPayload.data());
228 response->packetSeqNum = 0;
229 response->packetAckSeqNum = ackSeqNum;
230 response->acceptedCharCount = count;
231 response->outOperation.ack = ack;
232 sendPayload(outPayload);
233 return;
234 }
235
236 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
237 payloadCache.resize(sizeof(Payload) + readSize);
238 auto response = reinterpret_cast<Payload*>(payloadCache.data());
239 response->packetAckSeqNum = ackSeqNum;
240 response->acceptedCharCount = count;
241 response->outOperation.ack = ack;
242 response->packetSeqNum = seqNums.incOutboundSeqNum();
243
Vernon Mauery2085ae02021-06-10 11:51:00 -0700244 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph75e15db2017-03-14 18:16:22 +0530245 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
246 expectedCharCount = readSize;
247
Vernon Mauery6f353e82018-11-09 08:43:24 -0800248 enableRetryTimer(true);
249 enableAccumulateTimer(false);
Tom Joseph75e15db2017-03-14 18:16:22 +0530250
251 sendPayload(payloadCache);
252}
253
Tom Joseph73063142017-03-14 18:20:20 +0530254int Context::sendOutboundPayload()
255{
256 if (payloadCache.size() != 0)
257 {
Tom Joseph73063142017-03-14 18:20:20 +0530258 return -1;
259 }
260
Vernon Mauery2085ae02021-06-10 11:51:00 -0700261 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph73063142017-03-14 18:20:20 +0530262 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
263
264 payloadCache.resize(sizeof(Payload) + readSize);
265 auto response = reinterpret_cast<Payload*>(payloadCache.data());
266 response->packetAckSeqNum = 0;
267 response->acceptedCharCount = 0;
268 response->outOperation.ack = false;
269 response->packetSeqNum = seqNums.incOutboundSeqNum();
270
Vernon Mauery2085ae02021-06-10 11:51:00 -0700271 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph73063142017-03-14 18:20:20 +0530272 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
273 expectedCharCount = readSize;
274
Vernon Mauery6f353e82018-11-09 08:43:24 -0800275 enableRetryTimer(true);
276 enableAccumulateTimer(false);
Tom Joseph73063142017-03-14 18:20:20 +0530277
278 sendPayload(payloadCache);
279
280 return 0;
281}
282
Tom Joseph75e15db2017-03-14 18:16:22 +0530283void Context::resendPayload(bool clear)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530284{
Tom Joseph2fd466f2017-03-30 08:16:47 +0530285 sendPayload(payloadCache);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530286
Vernon Mauery9e801a22018-10-12 13:20:49 -0700287 if (clear)
Tom Joseph2fd466f2017-03-30 08:16:47 +0530288 {
289 payloadCache.clear();
290 expectedCharCount = 0;
Vernon Mauery2085ae02021-06-10 11:51:00 -0700291 sol::Manager::get().dataBuffer.erase(expectedCharCount);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530292 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530293}
294
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800295void Context::sendPayload(const std::vector<uint8_t>& out) const
Tom Josephfbcac2e2017-03-14 18:15:07 +0530296{
Tom Joseph22596f22017-03-31 10:52:27 +0530297 message::Handler msgHandler(session->channelPtr, sessionID);
298
299 msgHandler.sendSOLPayload(out);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530300}
301
Vernon Mauery6f353e82018-11-09 08:43:24 -0800302void Context::charAccTimerHandler()
303{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700304 auto bufferSize = sol::Manager::get().dataBuffer.size();
Vernon Mauery6f353e82018-11-09 08:43:24 -0800305
306 try
307 {
308 if (bufferSize > 0)
309 {
310 int rc = sendOutboundPayload();
311 if (rc == 0)
312 {
313 return;
314 }
315 }
316 enableAccumulateTimer(true);
317 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500318 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800319 {
George Liu7b7f25f2022-07-04 17:07:32 +0800320 lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
321 "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800322 }
323}
324
325void Context::retryTimerHandler()
326{
327 try
328 {
329 if (retryCounter)
330 {
331 --retryCounter;
332 enableRetryTimer(true);
333 resendPayload(sol::Context::noClear);
334 }
335 else
336 {
337 retryCounter = maxRetryCount;
338 resendPayload(sol::Context::clear);
339 enableRetryTimer(false);
340 enableAccumulateTimer(true);
341 }
342 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500343 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800344 {
George Liu7b7f25f2022-07-04 17:07:32 +0800345 lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800346 }
347}
Tom Josephfbcac2e2017-03-14 18:15:07 +0530348} // namespace sol