blob: 2dbdf60f472d99a64b2111850fd1e512a637dc74 [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();
Jian Zhang5499bf82024-01-24 15:33:11 +080047 std::weak_ptr<Context> weakRef = weak_from_this();
Tang Yiwei15535142022-09-21 17:28:15 +080048 if (bufferSize > sendThreshold)
49 {
Jian Zhang5499bf82024-01-24 15:33:11 +080050 getIo()->post([weakRef]() {
51 std::shared_ptr<Context> self = weakRef.lock();
52 if (self)
53 {
54 self->charAccTimerHandler();
55 }
56 });
Tang Yiwei15535142022-09-21 17:28:15 +080057 return;
58 }
Vernon Mauery6f353e82018-11-09 08:43:24 -080059 accumulateTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080060 accumulateTimer.async_wait(
61 [weakRef](const boost::system::error_code& ec) {
Patrick Williams099fb092023-05-10 07:50:31 -050062 std::shared_ptr<Context> self = weakRef.lock();
63 if (!ec && self)
64 {
65 self->charAccTimerHandler();
66 }
67 });
Vernon Mauery6f353e82018-11-09 08:43:24 -080068 }
69 else
70 {
71 accumulateTimer.cancel();
72 }
73}
74
75void Context::enableRetryTimer(bool enable)
76{
77 if (enable)
78 {
79 // fetch the timeout from the SOL manager
Vernon Mauery2085ae02021-06-10 11:51:00 -070080 std::chrono::microseconds interval = sol::Manager::get().retryInterval;
Vernon Mauery6f353e82018-11-09 08:43:24 -080081 retryTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080082 std::weak_ptr<Context> weakRef = weak_from_this();
83 retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
84 std::shared_ptr<Context> self = weakRef.lock();
85 if (!ec && self)
Vernon Mauery6f353e82018-11-09 08:43:24 -080086 {
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080087 self->retryTimerHandler();
Vernon Mauery6f353e82018-11-09 08:43:24 -080088 }
89 });
90 }
91 else
92 {
93 retryTimer.cancel();
94 }
95}
96
Vernon Mauery9e801a22018-10-12 13:20:49 -070097void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
Tingting Chenec437412022-09-27 18:35:22 +080098 uint8_t count, bool status, bool isBreak,
Vernon Mauery70fd29c2017-11-30 13:11:43 -080099 const std::vector<uint8_t>& input)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530100{
101 uint8_t respAckSeqNum = 0;
102 uint8_t acceptedCount = 0;
103 auto ack = false;
104
105 /*
106 * Check if the Inbound sequence number is same as the expected one.
107 * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
108 * outstanding sequence numbers are not supported in this version of the SOL
109 * specification. Retried packets use the same sequence number as the first
110 * packet.
111 */
Vernon Mauery9e801a22018-10-12 13:20:49 -0700112 if (seqNum && (seqNum != seqNums.get(true)))
Tom Josephfbcac2e2017-03-14 18:15:07 +0530113 {
George Liu7b7f25f2022-07-04 17:07:32 +0800114 lg2::info("Out of sequence SOL packet - packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530115 return;
116 }
117
118 /*
119 * Check if the expected ACK/NACK sequence number is same as the
120 * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
121 * number is 0, then it is an informational packet. No request packet being
122 * ACK'd or NACK'd.
123 */
124 if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
125 {
George Liu7b7f25f2022-07-04 17:07:32 +0800126 lg2::info("Out of sequence ack number - SOL packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530127 return;
128 }
129
130 /*
131 * Retry the SOL payload packet in the following conditions:
132 *
133 * a) NACK in Operation/Status
134 * b) Accepted Character Count does not match with the sent out SOL payload
135 * c) Non-zero Packet ACK/NACK Sequence Number
136 */
137 if (status || ((count != expectedCharCount) && ackSeqNum))
138 {
139 resendPayload(noClear);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800140 enableRetryTimer(false);
141 enableRetryTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530142 return;
143 }
144 /*
145 * Clear the sent data once the acknowledgment sequence number matches
146 * and the expected character count matches.
147 */
148 else if ((count == expectedCharCount) && ackSeqNum)
149 {
150 // Clear the Host Console Buffer
Vernon Mauery2085ae02021-06-10 11:51:00 -0700151 sol::Manager::get().dataBuffer.erase(count);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530152
153 // Once it is acknowledged stop the retry interval timer
Vernon Mauery6f353e82018-11-09 08:43:24 -0800154 enableRetryTimer(false);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530155
156 retryCounter = maxRetryCount;
157 expectedCharCount = 0;
158 payloadCache.clear();
159 }
160
Tingting Chenec437412022-09-27 18:35:22 +0800161 if (isBreak && seqNum)
162 {
163 lg2::info("Writing break to console socket descriptor");
164 constexpr uint8_t sysrqValue = 72; // use this to notify sol server
165 const std::vector<uint8_t> test{sysrqValue};
166 auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
167 if (ret)
168 {
169 lg2::error("Writing to console socket descriptor failed: {ERROR}",
170 "ERROR", strerror(errno));
171 }
172 }
173
174 isBreak = false;
Tom Josephfbcac2e2017-03-14 18:15:07 +0530175 // Write character data to the Host Console
176 if (!input.empty() && seqNum)
177 {
Tingting Chenec437412022-09-27 18:35:22 +0800178 auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530179 if (rc)
180 {
George Liu7b7f25f2022-07-04 17:07:32 +0800181 lg2::error("Writing to console socket descriptor failed: {ERROR}",
182 "ERROR", strerror(errno));
Tom Josephfbcac2e2017-03-14 18:15:07 +0530183 ack = true;
184 }
185 else
186 {
187 respAckSeqNum = seqNum;
188 ack = false;
189 acceptedCount = input.size();
190 }
191 }
Tom Joseph694fc0c2017-08-21 11:47:25 +0530192 /*
193 * SOL payload with no character data and valid sequence number can be used
194 * as method to keep the SOL session active.
195 */
196 else if (input.empty() && seqNum)
197 {
198 respAckSeqNum = seqNum;
199 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530200
201 if (seqNum != 0)
202 {
203 seqNums.incInboundSeqNum();
204 prepareResponse(respAckSeqNum, acceptedCount, ack);
205 }
206 else
207 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800208 enableAccumulateTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530209 }
210}
211
Tom Joseph75e15db2017-03-14 18:16:22 +0530212void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
213{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700214 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph75e15db2017-03-14 18:16:22 +0530215
216 /* Sent a ACK only response */
217 if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
218 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800219 enableAccumulateTimer(true);
Tom Joseph75e15db2017-03-14 18:16:22 +0530220
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800221 std::vector<uint8_t> outPayload(sizeof(Payload));
Tom Joseph75e15db2017-03-14 18:16:22 +0530222 auto response = reinterpret_cast<Payload*>(outPayload.data());
223 response->packetSeqNum = 0;
224 response->packetAckSeqNum = ackSeqNum;
225 response->acceptedCharCount = count;
226 response->outOperation.ack = ack;
227 sendPayload(outPayload);
228 return;
229 }
230
231 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
232 payloadCache.resize(sizeof(Payload) + readSize);
233 auto response = reinterpret_cast<Payload*>(payloadCache.data());
234 response->packetAckSeqNum = ackSeqNum;
235 response->acceptedCharCount = count;
236 response->outOperation.ack = ack;
237 response->packetSeqNum = seqNums.incOutboundSeqNum();
238
Vernon Mauery2085ae02021-06-10 11:51:00 -0700239 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph75e15db2017-03-14 18:16:22 +0530240 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
241 expectedCharCount = readSize;
242
Vernon Mauery6f353e82018-11-09 08:43:24 -0800243 enableRetryTimer(true);
244 enableAccumulateTimer(false);
Tom Joseph75e15db2017-03-14 18:16:22 +0530245
246 sendPayload(payloadCache);
247}
248
Tom Joseph73063142017-03-14 18:20:20 +0530249int Context::sendOutboundPayload()
250{
251 if (payloadCache.size() != 0)
252 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800253 enableAccumulateTimer(true);
Tom Joseph73063142017-03-14 18:20:20 +0530254 return -1;
255 }
256
Vernon Mauery2085ae02021-06-10 11:51:00 -0700257 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph73063142017-03-14 18:20:20 +0530258 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
259
260 payloadCache.resize(sizeof(Payload) + readSize);
261 auto response = reinterpret_cast<Payload*>(payloadCache.data());
262 response->packetAckSeqNum = 0;
263 response->acceptedCharCount = 0;
264 response->outOperation.ack = false;
265 response->packetSeqNum = seqNums.incOutboundSeqNum();
266
Vernon Mauery2085ae02021-06-10 11:51:00 -0700267 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph73063142017-03-14 18:20:20 +0530268 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
269 expectedCharCount = readSize;
270
Vernon Mauery6f353e82018-11-09 08:43:24 -0800271 enableRetryTimer(true);
272 enableAccumulateTimer(false);
Tom Joseph73063142017-03-14 18:20:20 +0530273
274 sendPayload(payloadCache);
275
276 return 0;
277}
278
Tom Joseph75e15db2017-03-14 18:16:22 +0530279void Context::resendPayload(bool clear)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530280{
Tom Joseph2fd466f2017-03-30 08:16:47 +0530281 sendPayload(payloadCache);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530282
Vernon Mauery9e801a22018-10-12 13:20:49 -0700283 if (clear)
Tom Joseph2fd466f2017-03-30 08:16:47 +0530284 {
285 payloadCache.clear();
286 expectedCharCount = 0;
Vernon Mauery2085ae02021-06-10 11:51:00 -0700287 sol::Manager::get().dataBuffer.erase(expectedCharCount);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530288 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530289}
290
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800291void Context::sendPayload(const std::vector<uint8_t>& out) const
Tom Josephfbcac2e2017-03-14 18:15:07 +0530292{
Tom Joseph22596f22017-03-31 10:52:27 +0530293 message::Handler msgHandler(session->channelPtr, sessionID);
294
295 msgHandler.sendSOLPayload(out);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530296}
297
Vernon Mauery6f353e82018-11-09 08:43:24 -0800298void Context::charAccTimerHandler()
299{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700300 auto bufferSize = sol::Manager::get().dataBuffer.size();
Vernon Mauery6f353e82018-11-09 08:43:24 -0800301
302 try
303 {
304 if (bufferSize > 0)
305 {
306 int rc = sendOutboundPayload();
307 if (rc == 0)
308 {
309 return;
310 }
311 }
312 enableAccumulateTimer(true);
313 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500314 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800315 {
George Liu7b7f25f2022-07-04 17:07:32 +0800316 lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
317 "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800318 }
319}
320
321void Context::retryTimerHandler()
322{
323 try
324 {
325 if (retryCounter)
326 {
327 --retryCounter;
328 enableRetryTimer(true);
329 resendPayload(sol::Context::noClear);
330 }
331 else
332 {
333 retryCounter = maxRetryCount;
334 resendPayload(sol::Context::clear);
335 enableRetryTimer(false);
336 enableAccumulateTimer(true);
337 }
338 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500339 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800340 {
George Liu7b7f25f2022-07-04 17:07:32 +0800341 lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800342 }
343}
Tom Josephfbcac2e2017-03-14 18:15:07 +0530344} // namespace sol