blob: 06dabcc50bfd415c0c537943e69db99b21425850 [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;
Vernon Mauery6f353e82018-11-09 08:43:24 -080043 if (enable)
44 {
45 accumulateTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080046 std::weak_ptr<Context> weakRef = weak_from_this();
47 accumulateTimer.async_wait(
48 [weakRef](const boost::system::error_code& ec) {
49 std::shared_ptr<Context> self = weakRef.lock();
50 if (!ec && self)
51 {
52 self->charAccTimerHandler();
53 }
54 });
Vernon Mauery6f353e82018-11-09 08:43:24 -080055 }
56 else
57 {
58 accumulateTimer.cancel();
59 }
60}
61
62void Context::enableRetryTimer(bool enable)
63{
64 if (enable)
65 {
66 // fetch the timeout from the SOL manager
Vernon Mauery2085ae02021-06-10 11:51:00 -070067 std::chrono::microseconds interval = sol::Manager::get().retryInterval;
Vernon Mauery6f353e82018-11-09 08:43:24 -080068 retryTimer.expires_after(interval);
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080069 std::weak_ptr<Context> weakRef = weak_from_this();
70 retryTimer.async_wait([weakRef](const boost::system::error_code& ec) {
71 std::shared_ptr<Context> self = weakRef.lock();
72 if (!ec && self)
Vernon Mauery6f353e82018-11-09 08:43:24 -080073 {
Vernon Mauerya6ad5e12020-02-21 14:54:24 -080074 self->retryTimerHandler();
Vernon Mauery6f353e82018-11-09 08:43:24 -080075 }
76 });
77 }
78 else
79 {
80 retryTimer.cancel();
81 }
82}
83
Vernon Mauery9e801a22018-10-12 13:20:49 -070084void Context::processInboundPayload(uint8_t seqNum, uint8_t ackSeqNum,
Tingting Chenec437412022-09-27 18:35:22 +080085 uint8_t count, bool status, bool isBreak,
Vernon Mauery70fd29c2017-11-30 13:11:43 -080086 const std::vector<uint8_t>& input)
Tom Josephfbcac2e2017-03-14 18:15:07 +053087{
88 uint8_t respAckSeqNum = 0;
89 uint8_t acceptedCount = 0;
90 auto ack = false;
91
92 /*
93 * Check if the Inbound sequence number is same as the expected one.
94 * If the Packet Sequence Number is 0, it is an ACK-Only packet. Multiple
95 * outstanding sequence numbers are not supported in this version of the SOL
96 * specification. Retried packets use the same sequence number as the first
97 * packet.
98 */
Vernon Mauery9e801a22018-10-12 13:20:49 -070099 if (seqNum && (seqNum != seqNums.get(true)))
Tom Josephfbcac2e2017-03-14 18:15:07 +0530100 {
George Liu7b7f25f2022-07-04 17:07:32 +0800101 lg2::info("Out of sequence SOL packet - packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530102 return;
103 }
104
105 /*
106 * Check if the expected ACK/NACK sequence number is same as the
107 * ACK/NACK sequence number in the packet. If packet ACK/NACK sequence
108 * number is 0, then it is an informational packet. No request packet being
109 * ACK'd or NACK'd.
110 */
111 if (ackSeqNum && (ackSeqNum != seqNums.get(false)))
112 {
George Liu7b7f25f2022-07-04 17:07:32 +0800113 lg2::info("Out of sequence ack number - SOL packet is dropped");
Tom Josephfbcac2e2017-03-14 18:15:07 +0530114 return;
115 }
116
117 /*
118 * Retry the SOL payload packet in the following conditions:
119 *
120 * a) NACK in Operation/Status
121 * b) Accepted Character Count does not match with the sent out SOL payload
122 * c) Non-zero Packet ACK/NACK Sequence Number
123 */
124 if (status || ((count != expectedCharCount) && ackSeqNum))
125 {
126 resendPayload(noClear);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800127 enableRetryTimer(false);
128 enableRetryTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530129 return;
130 }
131 /*
132 * Clear the sent data once the acknowledgment sequence number matches
133 * and the expected character count matches.
134 */
135 else if ((count == expectedCharCount) && ackSeqNum)
136 {
137 // Clear the Host Console Buffer
Vernon Mauery2085ae02021-06-10 11:51:00 -0700138 sol::Manager::get().dataBuffer.erase(count);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530139
140 // Once it is acknowledged stop the retry interval timer
Vernon Mauery6f353e82018-11-09 08:43:24 -0800141 enableRetryTimer(false);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530142
143 retryCounter = maxRetryCount;
144 expectedCharCount = 0;
145 payloadCache.clear();
146 }
147
Tingting Chenec437412022-09-27 18:35:22 +0800148 if (isBreak && seqNum)
149 {
150 lg2::info("Writing break to console socket descriptor");
151 constexpr uint8_t sysrqValue = 72; // use this to notify sol server
152 const std::vector<uint8_t> test{sysrqValue};
153 auto ret = sol::Manager::get().writeConsoleSocket(test, isBreak);
154 if (ret)
155 {
156 lg2::error("Writing to console socket descriptor failed: {ERROR}",
157 "ERROR", strerror(errno));
158 }
159 }
160
161 isBreak = false;
Tom Josephfbcac2e2017-03-14 18:15:07 +0530162 // Write character data to the Host Console
163 if (!input.empty() && seqNum)
164 {
Tingting Chenec437412022-09-27 18:35:22 +0800165 auto rc = sol::Manager::get().writeConsoleSocket(input, isBreak);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530166 if (rc)
167 {
George Liu7b7f25f2022-07-04 17:07:32 +0800168 lg2::error("Writing to console socket descriptor failed: {ERROR}",
169 "ERROR", strerror(errno));
Tom Josephfbcac2e2017-03-14 18:15:07 +0530170 ack = true;
171 }
172 else
173 {
174 respAckSeqNum = seqNum;
175 ack = false;
176 acceptedCount = input.size();
177 }
178 }
Tom Joseph694fc0c2017-08-21 11:47:25 +0530179 /*
180 * SOL payload with no character data and valid sequence number can be used
181 * as method to keep the SOL session active.
182 */
183 else if (input.empty() && seqNum)
184 {
185 respAckSeqNum = seqNum;
186 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530187
188 if (seqNum != 0)
189 {
190 seqNums.incInboundSeqNum();
191 prepareResponse(respAckSeqNum, acceptedCount, ack);
192 }
193 else
194 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800195 enableAccumulateTimer(true);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530196 }
197}
198
Tom Joseph75e15db2017-03-14 18:16:22 +0530199void Context::prepareResponse(uint8_t ackSeqNum, uint8_t count, bool ack)
200{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700201 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph75e15db2017-03-14 18:16:22 +0530202
203 /* Sent a ACK only response */
204 if (payloadCache.size() != 0 || (bufferSize < sendThreshold))
205 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800206 enableAccumulateTimer(true);
Tom Joseph75e15db2017-03-14 18:16:22 +0530207
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800208 std::vector<uint8_t> outPayload(sizeof(Payload));
Tom Joseph75e15db2017-03-14 18:16:22 +0530209 auto response = reinterpret_cast<Payload*>(outPayload.data());
210 response->packetSeqNum = 0;
211 response->packetAckSeqNum = ackSeqNum;
212 response->acceptedCharCount = count;
213 response->outOperation.ack = ack;
214 sendPayload(outPayload);
215 return;
216 }
217
218 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
219 payloadCache.resize(sizeof(Payload) + readSize);
220 auto response = reinterpret_cast<Payload*>(payloadCache.data());
221 response->packetAckSeqNum = ackSeqNum;
222 response->acceptedCharCount = count;
223 response->outOperation.ack = ack;
224 response->packetSeqNum = seqNums.incOutboundSeqNum();
225
Vernon Mauery2085ae02021-06-10 11:51:00 -0700226 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph75e15db2017-03-14 18:16:22 +0530227 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
228 expectedCharCount = readSize;
229
Vernon Mauery6f353e82018-11-09 08:43:24 -0800230 enableRetryTimer(true);
231 enableAccumulateTimer(false);
Tom Joseph75e15db2017-03-14 18:16:22 +0530232
233 sendPayload(payloadCache);
234}
235
Tom Joseph73063142017-03-14 18:20:20 +0530236int Context::sendOutboundPayload()
237{
238 if (payloadCache.size() != 0)
239 {
Vernon Mauery6f353e82018-11-09 08:43:24 -0800240 enableAccumulateTimer(true);
Tom Joseph73063142017-03-14 18:20:20 +0530241 return -1;
242 }
243
Vernon Mauery2085ae02021-06-10 11:51:00 -0700244 auto bufferSize = sol::Manager::get().dataBuffer.size();
Tom Joseph73063142017-03-14 18:20:20 +0530245 auto readSize = std::min(bufferSize, MAX_PAYLOAD_SIZE);
246
247 payloadCache.resize(sizeof(Payload) + readSize);
248 auto response = reinterpret_cast<Payload*>(payloadCache.data());
249 response->packetAckSeqNum = 0;
250 response->acceptedCharCount = 0;
251 response->outOperation.ack = false;
252 response->packetSeqNum = seqNums.incOutboundSeqNum();
253
Vernon Mauery2085ae02021-06-10 11:51:00 -0700254 auto handle = sol::Manager::get().dataBuffer.read();
Tom Joseph73063142017-03-14 18:20:20 +0530255 std::copy_n(handle, readSize, payloadCache.data() + sizeof(Payload));
256 expectedCharCount = readSize;
257
Vernon Mauery6f353e82018-11-09 08:43:24 -0800258 enableRetryTimer(true);
259 enableAccumulateTimer(false);
Tom Joseph73063142017-03-14 18:20:20 +0530260
261 sendPayload(payloadCache);
262
263 return 0;
264}
265
Tom Joseph75e15db2017-03-14 18:16:22 +0530266void Context::resendPayload(bool clear)
Tom Josephfbcac2e2017-03-14 18:15:07 +0530267{
Tom Joseph2fd466f2017-03-30 08:16:47 +0530268 sendPayload(payloadCache);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530269
Vernon Mauery9e801a22018-10-12 13:20:49 -0700270 if (clear)
Tom Joseph2fd466f2017-03-30 08:16:47 +0530271 {
272 payloadCache.clear();
273 expectedCharCount = 0;
Vernon Mauery2085ae02021-06-10 11:51:00 -0700274 sol::Manager::get().dataBuffer.erase(expectedCharCount);
Tom Joseph2fd466f2017-03-30 08:16:47 +0530275 }
Tom Josephfbcac2e2017-03-14 18:15:07 +0530276}
277
Vernon Mauery70fd29c2017-11-30 13:11:43 -0800278void Context::sendPayload(const std::vector<uint8_t>& out) const
Tom Josephfbcac2e2017-03-14 18:15:07 +0530279{
Tom Joseph22596f22017-03-31 10:52:27 +0530280 message::Handler msgHandler(session->channelPtr, sessionID);
281
282 msgHandler.sendSOLPayload(out);
Tom Josephfbcac2e2017-03-14 18:15:07 +0530283}
284
Vernon Mauery6f353e82018-11-09 08:43:24 -0800285void Context::charAccTimerHandler()
286{
Vernon Mauery2085ae02021-06-10 11:51:00 -0700287 auto bufferSize = sol::Manager::get().dataBuffer.size();
Vernon Mauery6f353e82018-11-09 08:43:24 -0800288
289 try
290 {
291 if (bufferSize > 0)
292 {
293 int rc = sendOutboundPayload();
294 if (rc == 0)
295 {
296 return;
297 }
298 }
299 enableAccumulateTimer(true);
300 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500301 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800302 {
George Liu7b7f25f2022-07-04 17:07:32 +0800303 lg2::error("Failed to call the sendOutboundPayload method: {ERROR}",
304 "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800305 }
306}
307
308void Context::retryTimerHandler()
309{
310 try
311 {
312 if (retryCounter)
313 {
314 --retryCounter;
315 enableRetryTimer(true);
316 resendPayload(sol::Context::noClear);
317 }
318 else
319 {
320 retryCounter = maxRetryCount;
321 resendPayload(sol::Context::clear);
322 enableRetryTimer(false);
323 enableAccumulateTimer(true);
324 }
325 }
Patrick Williams12d199b2021-10-06 12:36:48 -0500326 catch (const std::exception& e)
Vernon Mauery6f353e82018-11-09 08:43:24 -0800327 {
George Liu7b7f25f2022-07-04 17:07:32 +0800328 lg2::error("Failed to retry timer: {ERROR}", "ERROR", e);
Vernon Mauery6f353e82018-11-09 08:43:24 -0800329 }
330}
Tom Josephfbcac2e2017-03-14 18:15:07 +0530331} // namespace sol